1. 程式人生 > >bzoj4161 (k^2logn求線性遞推式)

bzoj4161 (k^2logn求線性遞推式)

ret n) log span isp for hide -h close

分析:

  我們可以寫把轉移矩陣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 }
View Code

bzoj4161 (k^2logn求線性遞推式)