题解 P1939 【【模板】矩阵加速(数列)】 矩阵快速幂
题目
其实这一题还是挺简单的(如果你会矩阵快速幂的话),不会的快去学啊!!! 好啦,回归正题,下面就讲一下本蒟蒻的拙见。
原理部分
由题知: - f[n]=f[n-1]+f[n-3]。我们只要从这个公式出发就好了。 - 我们假设有两个数组A[],B[]。
- A:f[3],f[2],f[1]。//A只有一行三列。
-
B: //B需要手推。
1 1 0 0 0 1 1 0 0
-如果我们让AB会有什么神奇的事发生呢? -聪明的你一定发现了 - AB=(f[3]+f[1]+0,f[3]+0+0,f[2]+0+0) =f[4],f[3],f[2] - 这时候我们把得到的结果再乘一个B数组 - 它就神奇的变为 f[5],f[4],f[3]啦! - 我们再乘,每个f的下表就加1。 于是,我们得到结论。
(f[n],f[n-1],f[n-2])*B=(f[n+1],f[n],f[n-1])。
我们最开始知道 f[3],f[2],f[1],把它当做A数组。
如果我们想知道f[x]的值,我们不是只要乘x-3个B就能得到答案了么。
嗯(too young too sample,哈哈),但我用递推也是推x-3次, 那我干嘛还要用矩阵加速呢?
别慌,我有矩阵快速幂!!!
虽然矩阵乘没有交换率(即A×B≠B×A),
但它有结合率啊(即(A×B)×C==A×(B×C))。
既然我们有x-3个B要乘,那我们可以先把它用快速幂求出来。
(什么,你不会快速幂,快去学啊。)
即求B^(x-3)。最后再乘A。
就可以通过减少相乘的次数来加快时间。
是不是恍然大悟了呢!
代码部分
(略丑,别怕。)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define ll long long ll p=1000000007; struct node{ ll map[4][4]; }e,ans; ll t,temp,answer=0; node mul(node a,node b)//用来两个矩阵相乘的函数 { node tp; memset(tp.map,0,sizeof(tp)); for(int i=1;i<=3;i++) for(int j=1;j<=3;j++) for(int z=1;z<=3;z++) tp.map[i][j]=(tp.map[i][j]+a.map[i][z]*b.map[z][j]%p)%p; return tp; } void quick (node a,ll b)//矩阵快速幂。 { memset(ans.map,0,sizeof(ans)); ans.map[1][1]=ans.map[2][2]=ans.map[3][3]=1;//设置单位矩阵,相当 //与普通快速幂里的ans //用来记录答案。 while(b) { if(b&1) ans=mul(ans,a); a=mul(a,a); b=(b>>1); } } int main() { memset(e.map,0,sizeof(e)); e.map[1][1]=e.map[1][2]=e.map[2][3]=e.map[3][1]=1;//B数组的初值。 scanf("%lld",&t); for(int i=1;i<=t;i++) { scanf("%lld",&temp); if(temp==1||temp==2||temp==3)//如果是求f[1],f[2],f[3]直接输出 { printf("%d\n",1); continue; } quick(e,temp-3);//求B^(x-3)。 printf("%lld\n",(ans.map[1][1]+ans.map[2][1]+ans.map[3][1])%p); //最后的A*B^(x-3)。由于只要f[x],我们手动相乘,不再操作f[x-1],f[x-2]。 } return 0; }
好啦,如果您有哪里不懂的话并且不嫌弃本蒟蒻的话,可以私信我哦。
有哪里讲的不对的话dalao一定指出来哦,望dalao斧正。