【学习笔记】常系数线性齐次递推

应用(LGP4723)

求一个满足\(k\)阶线性齐次递推数列\(a_i\)的第\(n\)项;

及:\(a_n \ = \ \sum_{i=1}^{k} f_i \times a_{n-i}\) ;

$N \le 10^9 \ , \ K \le 32000 $ ;

矩阵

  • 矩阵的秩\(rk(A)\)
    • \(rk(A) = 0 \ \Leftrightarrow \ A=0\) ;
    • \(rk(A+B) \le rk(A) + rk(B)\) ;
    • \(rk(AB) \le min(rk(A),rk(B))\) ;
  • 行列式按行展开:
    • \(det(A) \ = \sum_{j=1}^{n} a_{i,j}A_{i,j}\)
    • \(A_{i,j}\)为行列式的余子式;
    • 利用行列式的分拆性质可以证明;

特征多项式

  • 对于\(n\)阶方阵\(A\)
  • 若存在列向量\(v\)和常数\(\lambda\) ,满足 \(\lambda \vec{v} = A \ \vec{v}\);
  • 则称\(v\)\(A\)的特征向量;
  • \(k = rk(A)\),则存在\(k\)个线性无关的\(v\)
  • 变换形式:\((\lambda I - A) \vec{v} = 0\) ;
  • 有解的充要条件是\(det(\lambda I - A) = 0\);
  • 所以可以得到一个关于 $ \lambda $ 的 $ n $ 阶方程 $ F(\lambda)=0 $ , $ F(x) $ 称为 $ A $ 的特征多项式;
  • Cayley-Hamilton定理
  • 矩阵的特征多项式为矩阵的零化多项式:\(F(A) =0\)
  • 证明:https://blog.csdn.net/qq_35649707/article/details/78688235

线性递推

  • 设递推数列 \(a_{0} - a_{k-1}\) 的初始列向量为\(G\)(下标从下到上),\(A\)\(k\)阶递推矩阵,实际上就是要求\([A^nG]_{k,1}\)

  • 考虑如何求\(A^n\),注意到\(F(A)=0\)

  • \(A^n = P(A) F(A)+ Q(A)\);

  • 那么相当于\(A^n\)减去了很多个0矩阵,\(Q(A)\)和它意义相同,只需要算多项式\(Q(A)\)即可;

  • 这部分可以快速幂+多项式取模完成,复杂度\(O(k log \ k log \ m)\)或者$k ^ 2 log \ n $;

  • 考虑如何求\(A\)的特征多项式,利用定义$det(\lambda I - A) $的形式为:

    \[\begin{pmatrix} \lambda - f_{1} & -f_2 & -f_3 & \cdots & -f_{k-1} & -f_{k} \\ 1 & \lambda & 0 & \cdots & 0 & 0 \\ 0 & 1 & \lambda & \cdots & 0 & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ 0 & 0 & 0 & \cdots & \lambda & 0 \\ 0 & 0 & 0 & \cdots & 1 & \lambda \\ \end{pmatrix}* \]

  • 按第一行展开,会剩下一个下三角行列式,值为对角线乘积;

  • 得$F(\lambda) \ = \ \lambda^k - \sum_{i=1}^{k}f_i \lambda^{k-i} $ ;

  • \[g_{n} = [A^n G]_{k,1} = \sum_{i=0}^{k-1}[Q_{i} A^i \times G]_{k,1} =\sum_{i=0}^{k-1}Q_{i}[A^{i}G]_{k,1} = \sum_{i=0}^{k-1}Q_{i} a_{i} \]

  • 总复杂度就是求\(Q\)的复杂度,另外如果前\(k\)项没有给出的话似乎可以利用生成函数求;

    #include<bits/stdc++.h>
    #define mod 998244353 
    #define ll long long
    using namespace std;
    const int N=1000010;
    int n,k,m,f[N],h[N],a[N],b[N],ib[N];
    char gc(){
        static char*p1,*p2,s[1000000];
        if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin);
        return(p1==p2)?EOF:*p1++;
    }
    int rd(){
        int x=0,f=1;char c=gc();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc();}
        while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0',c=gc();}
        return x*f;
    }
    int pw(int x,int y){
        int re=1;
        if(y<0)y+=mod-1;
        while(y){
            if(y&1)re=(ll)re*x%mod;
            y>>=1;x=(ll)x*x%mod;
        }
        return re;
    }
    void inc(int&x,int y){x+=y;if(x>=mod)x-=mod;}
    namespace poly{
        const int G=3;
        int rev[N],L;
        void ntt(int*A,int len,int f){
            for(L=0;(1<<L)<len;++L);
            for(int i=0;i<len;++i){
                rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
                if(i<rev[i])swap(A[i],A[rev[i]]);
            }
            for(int i=1;i<len;i<<=1){
                int wn=pw(G,f*(mod-1)/(i<<1));
                for(int j=0;j<len;j+=i<<1){
                    int w=1;
                    for(int k=0;k<i;++k,w=(ll)w*wn%mod){
                        int x=A[j+k],y=(ll)w*A[j+k+i]%mod;
                        A[j+k]=(x+y)%mod,A[j+k+i]=(x-y+mod)%mod;
                    }
                }
            }
            if(!~f){
                int iv=pw(len,mod-2);
                for(int i=0;i<len;++i)A[i]=(ll)A[i]*iv%mod;
            }
        }
        void cls(int*A,int l,int r){for(int i=l;i<r;++i)A[i]=0;}
        void cpy(int*A,int*B,int l){for(int i=0;i<l;++i)A[i]=B[i];}
        void inv(int*A,int*B,int l){
            if(l==1){B[0]=pw(A[0],mod-2);return;}
            static int t[N];
            int len=l<<1;
            inv(A,B,l>>1);
            cpy(t,A,l);cls(t,l,len);
            ntt(t,len,1);ntt(B,len,1);
            for(int i=0;i<len;++i)B[i]=(ll)B[i]*(2-(ll)t[i]*B[i]%mod+mod)%mod;
            ntt(B,len,-1);cls(B,l,len);
        }
        void pmod(int*A){
            static int t[N];
            int l=k+1,len=1;while(len<=(k<<1))len<<=1;
            cpy(t,A,(k<<1)+1);
            reverse(t,t+(k<<1)+1);
            cls(t,l,len);
            ntt(t,len,1);
            for(int i=0;i<len;++i)t[i]=(ll)t[i]*ib[i]%mod;
            ntt(t,len,-1);
            cls(t,l,len);
            reverse(t,t+l);
            ntt(t,len,1);
            for(int i=0;i<len;++i)t[i]=(ll)t[i]*b[i]%mod;
            ntt(t,len,-1);
            cls(t,l,len);
            for(int i=0;i<k;++i)A[i]=(A[i]-t[i]+mod)%mod;
            cls(A,k,len);
        }
        void pow(int*A,int n){
            if(n==1){cls(A,0,k+1);A[1]=1;return;}
            pow(A,n>>1);
            int len=1;while(len<=(k<<1))len<<=1;
            ntt(A,len,1);
            for(int i=0;i<len;++i)A[i]=(ll)A[i]*A[i]%mod;
            ntt(A,len,-1);
            pmod(A);
            if(n&1){
                for(int i=k;i;--i)A[i]=A[i-1];A[0]=0;
                pmod(A);
            }
        }
    }
    int main(){
    //	freopen("P4723.in","r",stdin);
    //	freopen("P4723.out","w",stdout);
        n=rd();k=rd();
        for(int i=1;i<=k;++i)f[i]=(mod+rd())%mod;
        for(int i=0;i<k;++i)h[i]=(mod+rd())%mod;
        for(int i=a[k]=b[k]=1;i<=k;++i)a[k-i]=b[k-i]=(mod-f[i])%mod;
        int len=1;while(len<=(k<<1))len<<=1;
        reverse(a,a+k+1);
        poly::inv(a,ib,len);
        poly::cls(ib,k+1,len);
        /*{
            for(int i=0;i<len;++i)printf("%d ",b[i]);puts("");
            for(int i=0;i<len;++i)printf("%d ",ib[i]);puts("");
        }*/
        poly::ntt(b,len,1);
        poly::ntt(ib,len,1);
        poly::pow(a,n);
        int ans=0;
        for(int i=0;i<k;++i)inc(ans,(ll)a[i]*h[i]%mod);
        printf("%d\n",ans);
        return 0;
    }
    
    
posted @ 2019-03-29 07:50  大米饼  阅读(543)  评论(0编辑  收藏  举报