0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

TDPC T フィボナッチ kitamasa きたまさ

Last updated at Posted at 2020-06-04

##問題
https://atcoder.jp/contests/tdpc/tasks/tdpc_fibonacci
##見た感じ
Nがでかい、kもでかい、繰り返し自乗法でも間に合わない。
どうすればいいんだあ。
kitamasa法という線形漸化式を高速に計算する手法があるそうです。
いろいろテクニックを詰め込むとk次の線形漸化式で定まる数列のn項目が$O(k\log k \log n)$で求まるみたいですが、まずは簡単な方(それでも大変よ)の$O(k^2 \log n)$で計算できる手法を実装すべし。
kitamasa法についてはこのサイトを参考にしました。詳しいことはこのサイトに書いてあります。
kitamasa法は数列$a_n$の$a_1 ,...,a_k$による展開式の係数を知って入れば、$a_{n+1}$の展開したときの係数も$O(k)$で求まるし、$a_{2*n}$の展開したときの係数も$O(k^2 )$で求まるから、
これらを使って繰り返し自乗法していく方法です。

kitamasa
struct kitamasa{
    ll n;//次数
    vector<mint> a;//漸化式の係数
    vector<vector<mint>> mem;//a_n+i = \sum a_j mem[i][n-1-j]
    //kitamasa(漸化式の係数,次数)
    kitamasa(vector<mint> c,ll N) : a(c),n(N){
        vector<mint> r(2*n,0);
        rep(i,n)r[i]=c[i];
        mem.push_back(r);
        rep(i,n+1){
            mem.push_back(oneplass(mem[i]));
        }
    }
    //畳み込み
    vector<mint> closs(vector<mint> b){
        vector<mint> res(2*n,0);  
        vrep(i,2*n-2+1){
            mint a=0;
            rep(j,i+1){
                a+=b[j]*b[i-j];
            }
            res[i]=a;
        }
        return res;
    }
    //a_l の係数-> a_l+1 の係数
    vector<mint> oneplass(vector<mint> b){
        vector<mint> res(2*n,0);
        rep(i,n-1){
            res[i]=b[i+1]+b[0]*a[i];
        }
        res[n-1]=b[0]*a[n-1];
        return res;
    }
    //a_l の係数-> a_2*l の係数
    vector<mint> twotimes(vector<mint> b){
        vector<mint> res(2*n,0);
        auto bb=closs(b);
        rep(i,n-1){
            rep(j,n){
                bb[2*n-2-j]+=bb[n-2-i]*mem[i][n-1-j];
            }
            bb[n-2-i]=0;
        }
        rep(i,n){
            res[n-1-i]=bb[2*n-2-i];
        }
        return res;
    }
    //calculate(n)=a_nの係数
    vector<mint> calculate(int m){
        vector<mint> res(2*n,0);
        if(m<n){
            res[m]=a[m];
            return res;
        }
        if(m<=2*n){
            return mem[m-n];
        }else{
            if(m%2==0){
                auto b=calculate(m/2);
                res=twotimes(b);
            }else{
                auto b=calculate((m-1)/2);
                res=twotimes(b);
                res=oneplass(res);
            }
        }
        return res;
    }
};

##解法
kitamasa法を実装して使用
##ソースコード

struct kitamasa{
    ll n;//次数
    vector<mint> a;//漸化式の係数
    vector<vector<mint>> mem;//a_n+i = \sum a_j mem[i][n-1-j]
    //kitamasa(漸化式の係数,次数)
    kitamasa(vector<mint> c,ll N) : a(c),n(N){
        vector<mint> r(2*n,0);
        rep(i,n)r[i]=c[i];
        mem.push_back(r);
        rep(i,n+1){
            mem.push_back(oneplass(mem[i]));
        }
    }
    //畳み込み
    vector<mint> closs(vector<mint> b){
        vector<mint> res(2*n,0);  
        vrep(i,2*n-2+1){
            mint a=0;
            rep(j,i+1){
                a+=b[j]*b[i-j];
            }
            res[i]=a;
        }
        return res;
    }
    //a_l の係数-> a_l+1 の係数
    vector<mint> oneplass(vector<mint> b){
        vector<mint> res(2*n,0);
        rep(i,n-1){
            res[i]=b[i+1]+b[0]*a[i];
        }
        res[n-1]=b[0]*a[n-1];
        return res;
    }
    //a_l の係数-> a_2*l の係数
    vector<mint> twotimes(vector<mint> b){
        vector<mint> res(2*n,0);
        auto bb=closs(b);
        rep(i,n-1){
            rep(j,n){
                bb[2*n-2-j]+=bb[n-2-i]*mem[i][n-1-j];
            }
            bb[n-2-i]=0;
        }
        rep(i,n){
            res[n-1-i]=bb[2*n-2-i];
        }
        return res;
    }
    //calculate(n)=a_nの係数
    vector<mint> calculate(int m){
        vector<mint> res(2*n,0);
        if(m<n){
            res[m]=a[m];
            return res;
        }
        if(m<=2*n){
            return mem[m-n];
        }else{
            if(m%2==0){
                auto b=calculate(m/2);
                res=twotimes(b);
            }else{
                auto b=calculate((m-1)/2);
                res=twotimes(b);
                res=oneplass(res);
            }
        }
        return res;
    }
};
const int N_MAX=0;
ll n,k,a[1002];

int main(){
    int x,y;
    //入力
    cin>>k>>n;
    //処理
    vector<mint> b(k,1);
    kitamasa kita(b,k);
    
    auto res=kita.calculate(n-1);
    mint ans=0;
    rep(i,k){
        ans+=res[i];
    }
    //出力
    cout << ans <<endl;
}
0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?