【全国互虐】Fibonacci矩阵
orz啊又被屠了 人生如此艰难
题意:
给定一个k维的n^k的超立方体 超立方体的元素Ai1,i2,...,ik 的值为f(i1+i2+...+ik-k+1) f为斐波那契数列
求该超立方体的所有元素和
1<=n,k<=10^9
题解:
其实看到数据范围 就大概猜到是矩阵乘法了
但是我考试的时候想了半天还是不知道矩阵乘法怎么搞 - -
其实矩阵乘法比我想象中的厉害多了
这里有个性质 做完一维后 可以把这维压缩成一个点 用这维的和代替
并且压缩后下一维还是满足斐波那契的性质所以可以用同一个矩阵继续乘
那么把开始的[f[1],f[2],sum[1]] 改为[sum[n],sum[n]-f[1]+f[n+1],sum[n]] 继续快速幂即可
但是这样做的时间复杂度是O(klogn)的
其实上面的将[f[1],f[2],sum[1]] 改为[sum[n],sum[n]-f[1]+f[n+1],sum[n]]也是能用矩阵表示出来的orz
具体自己yy下 这样就能求出从这维转到下一维的矩阵是什么样的 这个矩阵的k次方就能求出答案
时间复杂度O(log(nk))
代码
1 #include <cstdio> 2 #include <cstring> 3 typedef long long ll; 4 struct info{ 5 ll n,m; 6 ll a[3][3]; 7 }save,jz,one,st; 8 const ll mo=1000000007; 9 ll t,n,m; 10 inline info operator*(info a,info b){ 11 info res; 12 res.n=a.n,res.m=b.m; 13 for (ll i=0;i<res.n;i++) 14 for (ll j=0;j<res.m;j++){ 15 res.a[i][j]=0; 16 for (ll k=0;k<a.m;k++) res.a[i][j]=(res.a[i][j]+a.a[i][k]*b.a[k][j]%mo)%mo; 17 } 18 return res; 19 } 20 void makeinfo(){ 21 memset(st.a,0,sizeof(st.a)); 22 memset(one.a,0,sizeof(one.a)); 23 memset(save.a,0,sizeof(save.a)); 24 st.n=1,st.m=3; 25 st.a[0][0]=1,st.a[0][1]=1,st.a[0][2]=1; 26 one.n=one.m=save.n=save.m=3; 27 one.a[0][0]=one.a[1][1]=one.a[2][2]=1; 28 save.a[0][1]=save.a[1][2]=save.a[1][0]=save.a[1][1]=save.a[2][2]=1; 29 } 30 info mi(info a,ll b){ 31 info res=one; 32 for (;b;b>>=1){ 33 if (b&1) res=res*a; 34 a=a*a; 35 } 36 return res; 37 } 38 int main(){ 39 freopen("fibonacci.in","r",stdin); 40 freopen("fibonacci.out","w",stdout); 41 scanf("%I64d",&t); 42 makeinfo(); 43 for (;t;t--){ 44 scanf("%I64d%I64d",&n,&m); 45 jz=mi(save,n-1); 46 jz.a[0][1]=jz.a[0][1]+jz.a[0][2]-1; 47 jz.a[1][1]=jz.a[1][1]+jz.a[1][2]; 48 jz.a[2][1]=jz.a[2][1]+jz.a[2][2]; 49 jz.a[0][0]=jz.a[0][2]; 50 jz.a[1][0]=jz.a[1][2]; 51 jz.a[2][0]=jz.a[2][2]; 52 jz=mi(jz,m); 53 jz=st*jz; 54 printf("%I64d\n",jz.a[0][0]); 55 } 56 fclose(stdin); 57 fclose(stdout); 58 }