常系数齐次线性递推学习笔记
定義
對于數列fff,如果有遞推式
fn=∑i=1kai×fn?i(n≥k)f_n=\sum_{i=1}^k a_i\times f_{n-i} \quad (n\geq k)fn?=i=1∑k?ai?×fn?i?(n≥k)
并且已知前kkk項,可稱其為kkk 階齊次線性遞推數列。
這個算法可以快速求出fff的第nnn項,復雜度為O(k2log?n)O(k^2\log n)O(k2logn),可優化至O(klog?klog?n)O(k\log k\log n)O(klogklogn)
前置知識
矩陣快速冪
不難看出矩陣快速冪可以解決這個問題
我們構造kkk轉移矩陣
A=(a1a2a3?ak?1ak100?00010?00001?00??????000?10)A=\left( \begin{matrix} a_1&a_2&a_3&\cdots&a_{k-1}&a_k\\ 1&0&0&\cdots&0&0\\ 0&1&0&\cdots&0&0\\ 0&0&1&\cdots&0&0\\ \vdots&\vdots&\vdots&\ddots&\vdots&\vdots\\ 0&0&0&\cdots&1&0 \end{matrix} \right)A=??????????a1?100?0?a2?010?0?a3?001?0????????ak?1?000?1?ak?000?0???????????
對于一個狀態
S=(fi+k?1fi+k?2??fi+1fi)S=\left( \begin{matrix} f_{i+k-1}\\ f_{i+k-2}\\ \vdots\\ \vdots\\ f_{i+1}\\ f_{i} \end{matrix} \right) S=???????????fi+k?1?fi+k?2???fi+1?fi?????????????
左乘上轉移矩陣即可得到下一個狀態
AS=(fi+kfi+k?1??fi+2fi+1)AS=\left( \begin{matrix} f_{i+k}\\ f_{i+k-1}\\ \vdots\\ \vdots\\ f_{i+2}\\ f_{i+1} \end{matrix} \right) AS=???????????fi+k?fi+k?1???fi+2?fi+1?????????????
我們只需要求出AnSA^nSAnS就可以算出答案
直接求的復雜度是O(k3log?n)O(k^3\log n)O(k3logn),不可接受
接下來將對矩陣快速冪進行優化
特征向量、值、多項式、方程
對于一個kkk階方陣AAA,如果有列向量xxx和實數λ\lambdaλ,滿足
Ax=λxAx=\lambda xAx=λx
我們稱xxx為AAA的特征向量,λ\lambdaλ為AAA對于xxx的特征值。
對這個式子變形
(λI?A)x=0(\lambda I-A)x=0(λI?A)x=0
其中III為kkk階單位矩陣
鬼知道我為什么要寫這個
我們記關于λ\lambdaλ的多項式
f(λ)=∣λI?A∣f(\lambda)=|\lambda I-A|f(λ)=∣λI?A∣
為AAA的特征多項式
f(λ)=0f(\lambda)=0f(λ)=0稱為特征方程,不過我們用不上
Caylay-Hamilton 定理
定理內容:對于方陣AAA,設f(λ)f(\lambda)f(λ)為其特征多項式,有f(A)=0f(A)=0f(A)=0
你沒看錯,f(A)f(A)f(A)是矩陣的多項式,就是把矩陣AAA代進去,計算a0I+a1A+a2A2+?+akAka_0I+a_1A+a_2A^2+\dots+a_kA^ka0?I+a1?A+a2?A2+?+ak?Ak得到一個零矩陣
證明有太多前置知識,我還不會,希望有生之年能補上。
代數余子式
余子式:一個矩陣AAA去掉Ai,jA_{i,j}Ai,j?所在的第iii行第jjj列后的行列式稱為余子式(對就是矩陣樹那樣),記為ai,ja_{i,j}ai,j?,是個具體的數。
代數余子式:(?1)i+jai,j(-1)^{i+j}a_{i,j}(?1)i+jai,j?
引理 矩陣的行列式等于它第一行的代數余子式之和。
對于第111行第iii個元素,因為是第一行,所以貢獻的逆序對個數為i?1i-1i?1,剛好和代數余子式的(?1)i+j(-1)^{i+j}(?1)i+j消掉,去掉第111行第iii列之后順序沒有任何影響,所以乘一個ai,ja_{i,j}ai,j?即可
算法流程
考慮之前的矩陣快速冪,最后推到了AnSA^nSAnS
我們考慮怎么快速求出AnA^nAn
我們發現AnA^nAn是一個矩陣的多項式,考慮前面的CH定理
我們設AAA的特征多項式為f(λ)f(\lambda)f(λ)
那么我們可以把AnA^nAn寫成
An=f(A)g(A)+h(A)A^n=f(A)g(A)+h(A)An=f(A)g(A)+h(A)
因為f(A)=0f(A)=0f(A)=0,所以
An=h(A)A^n=h(A)An=h(A)
也就是說,AnA^nAn和h(A)h(A)h(A)在矩陣意義上等價
那么如果我們知道AAA的特征多項式,我們就可以用一般意義下的多項式取模(具體做法后面講)算出h(A)h(A)h(A)的表達式,并且最高項次數不超過k?1k-1k?1
我們設求出的
h(x)=∑i=0k?1bixih(x)=\sum_{i=0}^{k-1}b_ix^ih(x)=i=0∑k?1?bi?xi
考慮我們要求的
AnSA^nSAnS
因為An=h(A)A^n=h(A)An=h(A),所以等于
∑i=0k?1biAiS\sum_{i=0}^{k-1}b_iA^iSi=0∑k?1?bi?AiS
AiSA^iSAiS相當于是第i+1i+1i+1個狀態,我們寫成矩陣形式
∑i=0k?1bi(fi+k?1fi+k?2??fi+1fi)=AnS=(fn+k?1fn+k?2??fn+1fn)\sum_{i=0}^{k-1}b_i\left(\begin{matrix} f_{i+k-1}\\ f_{i+k-2}\\ \vdots\\ \vdots\\ f_{i+1}\\ f_{i} \end{matrix}\right)=A^nS=\left(\begin{matrix} f_{n+k-1}\\ f_{n+k-2}\\ \vdots\\ \vdots\\ f_{n+1}\\ f_n \end{matrix}\right) i=0∑k?1?bi????????????fi+k?1?fi+k?2???fi+1?fi?????????????=AnS=???????????fn+k?1?fn+k?2???fn+1?fn?????????????
仔細觀察,發現我們只需要最下面的元素
所以
∑i=0k?1bifi=fn\sum_{i=0}^{k-1}b_if_i=f_ni=0∑k?1?bi?fi?=fn?
也就是把bbb和題目給的前kkk項對應乘起來就好了
現在的問題是如何求特征多項式
λI?A=(λ?a1?a2?a3?a4??ak?1?ak?1λ00?000?1λ0?0000?1λ?00???????0000?λ00000??1λ)\lambda I-A=\left(\begin{matrix} \lambda-a_1&-a_2&-a_3&-a_4&\cdots&-a_{k-1}&-a_k\\ -1&\lambda&0&0&\cdots&0&0\\ 0&-1&\lambda&0&\cdots&0&0\\ 0&0&-1&\lambda&\cdots&0&0\\ \vdots&\vdots&\vdots&\vdots&\ddots&\vdots&\vdots\\ 0&0&0&0&\cdots&\lambda&0\\ 0&0&0&0&\cdots&-1&\lambda \end{matrix}\right) λI?A=????????????λ?a1??100?00??a2?λ?10?00??a3?0λ?1?00??a4?00λ?00??????????ak?1?000?λ?1??ak?000?0λ?????????????
根據上面的引理,
∣λI?A∣=(λ?a1)a1,1+∑i=2k(?1)i+1(?ai)a1,i|\lambda I-A|=(\lambda-a_1)a_{1,1}+\sum_{i=2}^k(-1)^{i+1}(-a_i)a_{1,i}∣λI?A∣=(λ?a1?)a1,1?+i=2∑k?(?1)i+1(?ai?)a1,i?
其中ai,ja_{i,j}ai,j?為余子式
因為去掉第111行和任意一列后剩下的是一個下三角矩陣,并且對角線上刪除的列的左邊是?1-1?1,右邊是λ\lambdaλ,所以
a1,i=(?1)i?1λk?ia_{1,i}=(-1)^{i-1}\lambda^{k-i}a1,i?=(?1)i?1λk?i
所以
∣λI?A∣=(λ?a1)λk?1+∑i=2kaiλk?i=λk?∑i=1kaiλk?i|\lambda I-A|=(\lambda-a_1)\lambda^{k-1}+\sum_{i=2}^ka_i\lambda^{k-i}\\=\lambda^k-\sum_{i=1}^ka_i\lambda^{k-i}∣λI?A∣=(λ?a1?)λk?1+i=2∑k?ai?λk?i=λk?i=1∑k?ai?λk?i
即
f(λ)=λk?∑i=1kaiλk?if(\lambda)=\lambda^k-\sum_{i=1}^ka_i\lambda^{k-i}f(λ)=λk?i=1∑k?ai?λk?i
就是特征多項式
實現
前面的多項式取模部分是挖了坑的
An=f(A)g(A)+h(A)A^n=f(A)g(A)+h(A)An=f(A)g(A)+h(A)
我們已知f(A)f(A)f(A)為特征多項式,要求出余數h(A)h(A)h(A)
因為nnn很大,所以我們倍增計算并順便取模
常用的暴力取模好寫的多,復雜度O(k2log?n)O(k^2\log n)O(k2logn),具體實現見代碼注釋
BZOJ4161 Shlw loves matrixI
#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #define MAXN 4005//數組開兩倍 using namespace std; int n,k,F[MAXN],R[MAXN],H[MAXN]; const int MOD=1e9+7; typedef long long ll; inline int add(const int& x,const int& y){return x+y>=MOD? x+y-MOD:x+y;} inline void mul(int* f,int* g,int* to) {static int t[MAXN];memset(t,0,sizeof(t));for (int i=0;i<k;i++)for (int j=0;j<k;j++)t[i+j]=add(t[i+j],(ll)f[i]*g[j]%MOD);//求卷積for (int i=(k<<1);i>=k;t[i--]=0)for (int j=1;j<=k;j++) t[i-j]=add(t[i-j],(ll)t[i]*F[j]%MOD);//因為最高項次數為1,后面都是讀入的數的相反數,所以讀入不取負,這里的減改成加即可for (int i=0;i<k;i++) to[i]=t[i]; } inline void qpow(int* f,int p) {static int ans[MAXN];memset(ans,0,sizeof(ans));ans[0]=1;while (p){if (p&1) mul(f,ans,ans);mul(f,f,f);p>>=1;}for (int i=0;i<k;i++) f[i]=ans[i]; } int main() {scanf("%d%d",&n,&k);for (int i=1;i<=k;i++) scanf("%d",&F[i]),F[i]<0&&(F[i]+=MOD);for (int i=0;i<k;i++) scanf("%d",&H[i]),H[i]<0&&(H[i]+=MOD);R[1]=1;qpow(R,n);int ans=0;for (int i=0;i<k;i++) ans=add(ans,(ll)R[i]*H[i]%MOD);printf("%d\n",ans);return 0; }如果不幸遇到了毒瘤出題人,則需要用多項式全家桶中的多項式取模
復雜度為O(klog?klog?n)O(k\log k\log n)O(klogklogn),常數較大
Luogu P4723
#include <iostream> #include <cstdio> #include <cstring> #include <cctype> #include <algorithm> #define MAXN 131072 using namespace std; inline int read() {int ans=0,f=1;char c=getchar();while (!isdigit(c)) (c=='-')&&(f=-1),c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return f*ans; } const int MOD=998244353; typedef long long ll; inline int add(const int& x,const int& y){return x+y>=MOD? x+y-MOD:x+y;} inline int dec(const int& x,const int& y){return x<y? x-y+MOD:x-y;} inline int qpow(int a,int p) {int ans=1;while (p){if (p&1) ans=(ll)ans*a%MOD;a=(ll)a*a%MOD;p>>=1;}return ans; } #define inv(x) qpow(x,MOD-2) int rt[2][24]; int l,lim,r[MAXN]; inline void init(){lim=1<<l;for (int i=0;i<lim;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));} inline void NTT(int* a,int type) {for (int i=0;i<lim;i++) if (i<r[i]) swap(a[i],a[r[i]]);for (int L=0;L<l;L++){int mid=1<<L,len=mid<<1,Wn=rt[type][L+1];for (int s=0;s<lim;s+=len)for (int k=0,w=1;k<mid;k++,w=(ll)w*Wn%MOD){int x=a[s+k],y=(ll)w*a[s+mid+k]%MOD;a[s+k]=add(x,y);a[s+mid+k]=dec(x,y);}}if (type){int t=inv(lim);for (int i=0;i<lim;i++) a[i]=(ll)a[i]*t%MOD;} } void getinv(int* A,int* B,int n) {static int f[MAXN],t[MAXN];if (n==1) return (void)(*B=inv(*A));getinv(A,t,(n+1)>>1);for (l=0;(1<<l)<(n<<1);l++);init();for (int i=0;i<n;i++) f[i]=A[i];for (int i=n;i<lim;i++) f[i]=t[i]=0;NTT(f,0);NTT(t,0);for (int i=0;i<lim;i++) B[i]=(ll)t[i]*dec(2,(ll)f[i]*t[i]%MOD)%MOD;NTT(B,1);for (int i=n;i<lim;i++) B[i]=0; } void getmod(int* A,int* B,int n,int m) {static int f[MAXN],g[MAXN],h[MAXN],t[MAXN];for (int i=0;i<=n;i++) f[i]=A[n-i];for (int i=0;i<=m;i++) g[i]=B[m-i];getinv(g,t,n-m+1);for (int i=n-m+1;i<lim;i++) f[i]=0;NTT(f,0);NTT(t,0);for (int i=0;i<lim;i++) h[i]=(ll)f[i]*t[i]%MOD;NTT(h,1);for (int i=n-m+1;i<lim;i++) h[i]=0;reverse(h,h+n-m+1),reverse(g,g+m+1);for (l=0;(1<<l)<=n;l++);init();for (int i=m+1;i<lim;i++) g[i]=0;for (int i=n-m+1;i<lim;i++) h[i]=0;NTT(g,0);NTT(h,0);for (int i=0;i<lim;i++) g[i]=(ll)g[i]*h[i]%MOD;NTT(g,1);for (int i=0;i<m;i++) A[i]=dec(A[i],g[i]);for (int i=m;i<lim;i++) A[i]=0; } int n,k; inline void mul(int* A,int* B,int* C,int* M) {static int f[MAXN],g[MAXN];for (int i=0;i<k;i++) f[i]=A[i],g[i]=B[i];for (l=0;(1<<l)<(k<<1);l++);init();for (int i=k;i<lim;i++) f[i]=g[i]=0;NTT(f,0);NTT(g,0);for (int i=0;i<lim;i++) C[i]=(ll)f[i]*g[i]%MOD;NTT(C,1);getmod(C,M,(k-1)<<1,k); } inline void qpow(int* A,int* R,int p) {static int ans[MAXN];memset(ans,0,sizeof(ans));ans[0]=1;while (p){if (p&1) mul(ans,A,ans,R);mul(A,A,A,R);p>>=1;}for (int i=0;i<k;i++) A[i]=ans[i]; } int f[MAXN],a[MAXN],h[MAXN]; int main() {rt[0][23]=qpow(3,119);rt[1][23]=inv(rt[0][23]);for (int i=23;i>=1;i--) rt[0][i-1]=(ll)rt[0][i]*rt[0][i]%MOD,rt[1][i-1]=(ll)rt[1][i]*rt[1][i]%MOD;n=read(),k=read();for (int i=1;i<=k;i++) (f[k-i]=-read())<0&&(f[k-i]+=MOD);for (int i=0;i<k;i++) (a[i]=read())<0&&(a[i]+=MOD);f[k]=1;h[1]=1;qpow(h,f,n);int ans=0;for (int i=0;i<k;i++) ans=add(ans,(ll)h[i]*a[i]%MOD);printf("%d\n",ans);return 0; }總結
以上是生活随笔為你收集整理的常系数齐次线性递推学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【BZOJ 4671】异或图 【斯特林反
- 下一篇: 郭明錤发布 2024 款苹果 iPad