bzoj4161 (k^2logn求线性递推式)
分析:
我们可以写把转移矩阵A写出来,然后求一下它的特征多项式,经过手动计算应该是这样的p(x)=$x^k-\sum\limits_{i=1}^ka_i*x^{k-i}$
根据Cayley-Hamilton定理可得,p(A)=0
他表示$A^n = f(A) * p(A) + g(A)$
第一项的值是0,所以即$A^n=g(A)$,其中f(A) g(A)都是关于A的多项式,f(A)是多项式除法的商,g(A)是余数
我们考虑$x^n$这个多项式,我们去求出它对于$p(A)$的余数多项式$g(A)$,那么$A^n$就等价于了$g(A)$,注意到新的多项式次数就很低了,不超过k-1
我们要求的是$A^nH=\sum\limits_{i=0}^{k-1}c_i*A^i*H$的第一个元素,注意到$A^i*H$相当于把H又递推了i次
所以结果等价于$A^nH=\sum\limits_{i=0}^{k-1}c_i*A^i*H$
时间复杂度$O(k^2 log n)$,但常数很大
中间的多项式取模和递推可以用FFT来优化,但常数更巨大
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=4000,mod=1000000007; 4 int a[maxn+5],p[maxn+5],ans[maxn+5],num[maxn+5]; 5 int h[maxn+5],tmp[maxn+5]; 6 int n,k; 7 void mul(int *a,int *b,int *ans) 8 { 9 for(int i=0;i<=2*k;++i) tmp[i]=0; 10 for(int i=0;i<k;++i) 11 for(int j=0;j<k;++j) 12 tmp[i+j]=(tmp[i+j]+1LL*a[i]*b[j])%mod; 13 for(int i=2*k-2;i>=k;--i) 14 { 15 for(int j=k-1;j>=0;--j) 16 tmp[i-k+j]=(tmp[i-k+j]-1LL*tmp[i]*p[j])%mod,tmp[i-k+j]=(tmp[i-k+j]+mod)%mod; 17 tmp[i]=0; 18 } 19 for(int i=0;i<k;++i) ans[i]=tmp[i]; 20 } 21 int main() 22 { 23 scanf("%d%d",&n,&k); 24 for(int i=1;i<=k;++i) scanf("%d",&a[i]); 25 for(int i=0;i<k;++i) scanf("%d",&h[i]); 26 p[k]=1; 27 for(int i=1;i<=k;++i) p[k-i]=mod-a[i]; 28 for(int i=k;i<2*k;++i) 29 for(int j=1;j<=k;++j) 30 { 31 h[i]=h[i]+1LL*h[i-j]*a[j]%mod; 32 h[i]%=mod; 33 } 34 if(n<2*k) return 0*printf("%d\n",h[n]); 35 int b=n-k+1; 36 num[1]=1,ans[0]=1; 37 while(b) 38 { 39 if(b&1) mul(ans,num,ans); 40 mul(num,num,num); 41 b>>=1; 42 } 43 long long res=0; 44 for(int i=0;i<k;++i) res=(res+1LL*ans[i]*h[i+k-1])%mod; 45 printf("%lld\n",(res+mod)%mod); 46 return 0; 47 }