常系数齐次线性递推-矩阵快速幂的优化-学习笔记

i207M

2019-01-26 20:48:57

Personal

常系数:系数都是常数 齐次:没有常数项 线性:没有高次项 通常的解法:矩阵快速幂,$O(k^3\log k)$ 我们有优化的方法。 考虑通常的解法: 根据矩阵乘法的结合律,易得到: $$\left( \begin{bmatrix} f_1 &f_2 &f_3 &f_4 & \cdots &f_{k-2} &f_{k-1} \\ 1 &0 &0 &0 & \cdots &0 &0 \\ 0 &1 &0 &0 & \cdots &0 &0\\ \cdots & \cdots& \cdots & \cdots & \cdots & \cdots & \cdots\\ 0 &0 &0 &0 & \cdots &1 &0 \end{bmatrix} \right) ^n \times \begin{bmatrix} a_{k-1} \\ a_{k-2} \\ \cdots \\ a_{1} \\ a_{0}\end{bmatrix} =\begin{bmatrix} a_{n+k-1} \\ a_{n+k-2} \\ \cdots \\ a_{n+1} \\ a_{n}\end{bmatrix}$$ 所以我们只需要算出 $M^n \times A$,然后取最后一个数就可以了。 我们除了快速幂还有什么方法吗? 我们能不能有一个取巧的办法,使得我们只需要算低次的矩阵乘法? 我们可以将 $M^n$分解为 $M^n=f(M) \times g(M) +R(M)$,且$f(M)=O$ ,所以 $M^n=R(M)$ , $R(M)$ 是一个次数不超过 k-1 的多项式。 也就是说,我们只要求出来 $M^n \mod f(M)$就万事大吉了。 我们可以多项式快速幂+多项式取模来做,但是,怎么求出$f(M)$呢? ------ 我们先准备一些知识: ![](https://cdn.luogu.com.cn/upload/pic/49849.png) 则有 ### Cayley-Hamilton定理 $f(A)=O$($f$指特征多项式) 证明的话很简单啊,对一个0矩阵求行列式当然是0。 好的,我们会构造了,但是如何优雅地求出它每一项的系数? 注意我们的矩阵是一个上海森堡矩阵。(但这并没有什么用) 我们把矩阵的第一行拉普拉斯展开一下 $f(\lambda)=(\lambda-a_1)A_{1,1}+(-a_2)A_{1,2}+(-a_3)A_{1.3}...+(-a_n)A_{1,n}$ 其中$A_{i,j}$表示A的代数**余子式** 余子式的简单理解就是把$(i,j)$所在的行列去掉,剩下的$n-1$阶矩阵的行列式。 显然所有余子式都是**下三角矩阵**,行列式很容易求得 那么我们得到了$f(\lambda)=\lambda^n-\sum_{i=1}^{n}a_i\lambda^{n-i}$ ------ 于是就当我们算出了$M^n \mod f(M)$,然后怎么计算呢?暴力算出矩阵?复杂度又爆炸了。考虑我们最后要求什么? 我们把多项式的每一项单独考虑,对于$k$次项。我们有一个初值的列向量,还有一个$A^k$乘在一起,还有一个$c_k$作为系数;我们只要求出计算后的第一个位置的值即可。注意前两者的乘积,就是$a_k$!所以我们计算一下$\sum a_kc_k$即可! ~~打算WC回去的火车上写~~ ```cpp void _Inv(int n,int a[],int b[]) { if(n==1) { b[0]=qpow(a[0],md-2); return; } _Inv(n>>1,a,b); static int c[N]; for(ri i=0; i<n; ++i) c[i]=a[i]; int m=pre(n<<1); dft(b,m,1),dft(c,m,1); for(ri i=0; i<m; ++i) b[i]=(2-(LL)b[i]*c[i]%md+md)*b[i]%md; dft(b,m,-1); for(ri i=n; i<m; ++i) b[i]=0; for(ri i=0; i<m; ++i) c[i]=0; } void Inv(int n,int a[],int b[]) { int m=1; for(; m<n; m<<=1); _Inv(m,a,b); for(ri i=n; i<m; ++i) b[i]=0; } void Div(int n,int a[],int m,int b[],int d[]) { static int tmpa[N],tmpb[N],tmpc[N]; int len=n-m+1; for(ri i=0; i<len; ++i) tmpa[i]=a[n-i-1]; for(ri i=0; i<m; ++i) tmpb[i]=b[m-i-1]; Inv(len,tmpb,tmpc); int t=pre(len<<1); dft(tmpa,t,1),dft(tmpc,t,1); for(ri i=0; i<t; ++i) tmpc[i]=(LL)tmpa[i]*tmpc[i]%md; dft(tmpc,t,-1); reverse(tmpc,tmpc+len); for(ri i=0; i<t; ++i) tmpa[i]=0; for(ri i=0; i<m; ++i) tmpb[i]=b[i]; for(ri i=0; i<len; ++i) d[i]=tmpc[i]; for(ri i=len; i<t; ++i) d[i]=0; for(ri i=0; i<t; ++i) tmpc[i]=0; t=pre(n<<1); dft(tmpb,t,1),dft(d,t,1); for(ri i=0; i<t; ++i) d[i]=(LL)tmpb[i]*d[i]%md; dft(d,t,-1); for(ri i=0; i<m-1; ++i) d[i]=(a[i]-d[i]+md)%md; for(ri i=m-1; i<t; ++i) d[i]=0; for(ri i=0; i<t; ++i) tmpb[i]=0; } void mul(int n,int a[],int b[],int Md[]) { static int c[N]; for(ri i=0; i<n; ++i) c[i]=b[i]; int m=pre(n<<1); dft(a,m,1),dft(c,m,1); for(ri i=0; i<m; ++i) a[i]=(LL)a[i]*c[i]%md; dft(a,m,-1); for(ri i=0; i<m; ++i) c[i]=0; Div(m,a,n+1,Md,c); for(ri i=0; i<n; ++i) a[i]=c[i]; for(ri i=n; i<m; ++i) a[i]=0; } void Qpow(int a[],int b,int res[],int m,int Md[]) { res[0]=1; for(; b; b>>=1,mul(m,a,a,Md)) if(b&1) mul(m,res,a,Md); } int n,k; int a[N],f[N],c[N],d[N]; signed main() { #ifdef M207 freopen("in.in","r",stdin); // freopen("out.out","w",stdout); #endif in(n,k); a[k]=1; for(ri i=1; i<=k; ++i) in(a[k-i]); for(ri i=0; i<k; ++i) a[i]=(md-a[i]%md)%md; for(ri i=0; i<k; ++i) in(f[i]),f[i]=(f[i]%md+md)%md; c[1]=1; Qpow(c,n,d,k,a); int ans=0; for(ri i=0; i<k; ++i) ans=(ans+(LL)d[i]*f[i])%md; out(ans); return 0; } ```