基本的なアイデアは本番中に思いついたものの、最後まで解ききれなかった。
http://codeforces.com/contest/392/problem/C
問題
F[1]=1、F[2]=2、F[i]=F[i-1]+F[i-2]で定義されるフィボナッチ数列を考える。
N,Kが与えられるのでを答えよ。
解法
かなり大きな添え字のフィボナッチ数列、ときたら行列の累乗で解くことがすぐ思い浮かぶ。
問題は行列の値である。
を漸化式で表現すると、
よってはおよびの定数倍の和で表現できる。
以下の84x84の行列では、最初41行に、次の41行にの係数、83行目に、84行目にの係数を配置している。
そしてその行列を(N-1)乗して、(ただしj==0の時は1)を要素に持つベクトルと掛け合わせて答えを求めた。
例えばK==4の時の行列は以下のようになる。
84x84全部は大きいので省略して、先頭6行がF[i]*i^j、次の6行がF[i-1]*(i-1)^j、最後2行がS[i]とS[i-1]を表している。
1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 0 0 2 1 0 0 0 0 0 0 1 2 1 0 0 0 4 4 1 0 0 0 0 0 1 3 3 1 0 0 8 12 6 1 0 0 0 0 1 4 6 4 1 0 16 32 24 8 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 4 6 4 1 0 16 32 24 8 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
計算量は、行列の大きさがO(K)なので累乗処理でO(logN K^3)。
ll N,K; ll mo=1000000007; const int CN=1001; ll C[CN][CN]; const int MAT=85; ll G[MAT][MAT],G2[MAT][MAT]; void powmat(ll p,int n,ll a[MAT][MAT],ll r[MAT][MAT]) { int i,x,y,z; ll a2[MAT][MAT]; FOR(x,n) FOR(y,n) a2[x][y]=a[x][y]; FOR(x,n) FOR(y,n) r[x][y]=0; FOR(i,n) r[i][i]=1; while(p) { ll h[MAT][MAT]; if(p%2) { FOR(x,n) FOR(y,n) { h[x][y]=0; FOR(z,n) h[x][y] += (r[x][z]*a2[z][y]) % mo; h[x][y] %= mo; } FOR(x,n) FOR(y,n) r[x][y]=h[x][y]; } FOR(x,n) FOR(y,n) { h[x][y]=0; FOR(z,n) h[x][y] += (a2[x][z]*a2[z][y]) % mo; h[x][y] %= mo; } FOR(x,n) FOR(y,n) a2[x][y]=h[x][y]; p>>=1; } } void solve() { int f,i,j,k,l,x,y; cin>>N>>K; if(N==1) return _P("1\n"); FOR(x,CN) C[x][0]=C[x][x]=1; for(i=1;i<CN;i++) for(j=1;j<i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo; FOR(i,K+1) { FOR(j,i+1) { G[i][j]=C[i][j]; FOR(l,j+1) G[i][l+41]+=C[i][j]*C[j][l],G[i][l+41]%=mo; } } memmove(G[82],G[K],sizeof(G[K])); for(i=41;i<82;i++) G[i][i-41]=1; G[82][82]=1; G[83][82]=1; powmat(N-1,84,G,G2); FOR(i,41) ret+=G2[82][i]; ret+=G2[82][82]+G2[82][41]; cout << ret%mo << endl; }
まとめ
行列表現までは思いついたけど、この行列は思いつかなかった…。