BZOJ3286 Fibonacci矩阵 矩阵 快速幂 卡常
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ3286
题意概括
n,m,a,b,c,d,e,f<=10^1000000
题解
神奇的卡常题目。
在此感谢"zhouzixuan"——bzoj 3286: Fibonacci矩阵
学习他,才15秒卡过此题。
这题的做法应该很明显的,学过矩阵快速幂的大概几眼就看出来了。
对于每一行的转移,是相同的,所以矩阵快速幂可以搞定行与行之间的转移。
然后对于某一行,其实大部分的转移是和abc有关的,同理也可用矩阵快速幂搞定。
但是如果折半的话,矩阵快速幂的复杂度为log2(n)*len(n) (高精度运算复杂度)
要TLE。
然后我们发现,如果10位10位走,就不用跑高精度了。那么时间复杂度理论上就可以过去了。
但是这题神坑。
卡常!!!!!!!
那么,我们研究矩阵,发现,我们构造的矩阵,有一列(或者一行)的3个数的值总是不变的。
那么,我们可以减少循环??
然后,去掉循环,手写9个运算式又可以省去循环变量的几个运算的复杂度。
然后,仍然不够。我们发现数组访问有点慢,那么我们把数组去掉,改成一个一个的变量。
这样大概可以快上4倍。
然后,我们发现之前的那3个不变的也没用了,直接不运算了,其余的按照原先的运算,其中涉及之前的那3个的就把变量名改成值就可以了。
然后发现还是TLE了。
您千万不要气馁。
对于矩阵快速幂中,我原先的版本是这样的:
Mat MatPow(Mat x,int *v,int len){
Mat ans(1),xx;
for (int i=1;i<=len;i++){
for (int j=v[i];j--;)
ans=ans*x;
xx=x=x*x,x=x*x,x=x*x,x=x*xx;
}
return ans;
}
其实,这个时候,我们不仅要在x上面花时间,又要在ans上花时间,所以,我们要想办法卡。
事实上,我们可以这样:(省掉了约1/3的时间)
inline Mat MatPow(Mat x,int *v,int len){
Mat ans(1),xx;
Mat fac[10];
fac[0].set(1);
for (int i=1;i<=9;i++)
fac[i]=fac[i-1]*x;
for (int i=len;i>=1;i--)
ans=MatPow(ans)*fac[v[i]];
return ans;
}
然后我就过去了。(心)累死了。
update 2017-12-22
在写BZOJ3240的时候,突然发现我当初在写最后的MatPow的时候,有重复计算。然后舍去了重复计算,卡掉了1/3的常数,现在是10秒了。
代码
#include <cstring> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cmath> using namespace std; typedef long long LL; const int mod=2012182013; struct BigInt{ static const int MaxL=1000005; int v[MaxL],len; inline bool isd(char ch){return '0'<=ch&&ch<='9';} inline void read(){ len=0; char ch=getchar(); while (!isd(ch)) ch=getchar(); while (isd(ch)) v[++len]=ch-48,ch=getchar(); for (int i=1;i<=len/2;i++) swap(v[i],v[len+1-i]); } inline void minus(){ v[1]--; for (int i=1;i<len;i++) if (v[i]<0) v[i]+=10,v[i+1]--; else break; while (len&&!v[len]) len--; } }; inline bool isd(char ch){return '0'<=ch&&ch<='9';} inline LL getMODed(){ char ch=getchar(); while (!isd(ch)) ch=getchar(); LL res=0; while (isd(ch)) res=(res*10+ch-48)%mod,ch=getchar(); return res; } struct Mat{ LL v11,v12,v21,v22,v31,v32; inline Mat (){} inline Mat (int x){(*this).set(x);} inline void set(int x){ v11=v12=v21=v22=v31=v32=0; if (x==1) v11=v22=1; } inline void build(int a,int b,int c){ v11=0,v12=a; v21=1,v22=b; v31=0,v32=c; } inline Mat operator * (Mat &b){ Mat ans; ans.v11=(v11*b.v11+v12*b.v21)%mod; ans.v12=(v11*b.v12+v12*b.v22)%mod; ans.v21=(v21*b.v11+v22*b.v21)%mod; ans.v22=(v21*b.v12+v22*b.v22)%mod; ans.v31=(v31*b.v11+v32*b.v21+b.v31)%mod; ans.v32=(v31*b.v12+v32*b.v22+b.v32)%mod; return ans; } }; inline Mat MatPow(Mat x){ Mat ans(1); for (int y=10;y;y>>=1,x=x*x) if (y&1) ans=ans*x; return ans; } inline Mat MatPow(Mat x,int *v,int len){ Mat ans(1),xx; Mat fac[10]; fac[0].set(1); for (int i=1;i<=9;i++) fac[i]=fac[i-1]*x; for (int i=len;i>=1;i--) ans=MatPow(ans)*fac[v[i]]; return ans; } LL a,b,c,d,e,f; BigInt n,m; Mat Mx,My,en,res; int main(){ n.read(),m.read(); a=getMODed(),b=getMODed(),c=getMODed(); d=getMODed(),e=getMODed(),f=getMODed(); Mx.build(a,b,c),My.build(d,e,f); m.minus(),m.minus(),n.minus(); res=MatPow(Mx,m.v,m.len); en=MatPow(res*My*My,n.v,n.len)*res; printf("%lld",(en.v12+en.v22+en.v32)%mod); return 0; }