zoukankan      html  css  js  c++  java
  • 常系数齐次线性递推

    矩阵乘法可以被优化成$k^2logn$的。

    设转移矩阵是$M$,那么$M$的特征多项式就是 $| lambda E−M| = lambda ^ k -sum_{i=1}^{k}a[i]* lambda ^{k-i}$

    $f(lambda)$ 是化零多项式,即有 $f(M) = 0$.

    把矩阵带进去,得到$M^k=sum_{i=1}^{k}a[i]*M^{k-i}$,这样我们可以把任意$M^n$表示为$E~M^{k-1}$的和。

    $M^n=M^{ lfloor frac{n}{2} floor} imes M^{n- lfloor frac{n}{2} floor}$,做多项式乘法,得到一个$2k-2$次的多项式,然后不停往前推,直到剩余一个$k-1$次多项式。

    最后得到$XM^n=Xsum_{i=0}^{k-1}c_iM^i$,因为$M^iX={h_i,h_{i+1},...,h_{i+k-1}}$,暴力加起来就行了。

    大概搞个多项式取模就可以优化成$klogklogn$的。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define LL long long
    const int p=1000000007;
    int n,k,f[2010],h[4010];
    int rd()
    {
        int x=0,f=1;
        char c=getchar();
        while (c<'0'||c>'9')
        {
            if (c=='-') f=-1;
            c=getchar();
        }
        while (c>='0'&&c<='9')
        {
            x=x*10+c-'0';
            c=getchar();
        }
        return x*f;
    }
    struct mat
    {
        int a[4010];
        void init()
        {
            a[0]=1;
            for (int i=1;i<k;i++) a[i]=0;
        }
        mat operator * (const mat &mm) const
        {
            mat ret;
            for (int i=0;i<=2*k-2;i++) ret.a[i]=0;
            for (int i=0;i<k;i++)
                for (int j=0;j<k;j++)
                    ret.a[i+j]=(ret.a[i+j]+(LL)a[i]*mm.a[j])%p;
            for (int i=2*k-2;i>=k;i--)
                for (int j=1;j<=k;j++)
                    ret.a[i-j]=(ret.a[i-j]+(LL)ret.a[i]*f[j])%p;
            return ret;
        }
    }base,res;
    int main()
    {
        int ans=0;
        n=rd();
        k=rd();
        for (int i=1;i<=k;i++)
        {
            f[i]=rd();
            if (f[i]<0) f[i]+=p;
        }
        for (int i=0;i<k;i++)
        {
            h[i]=rd();
            if (h[i]<0) h[i]+=p;
        }
        res.init();
        base.a[1]=1;
        for (n-=k-1;n;n>>=1,base=base*base)
            if (n&1) res=res*base;
        for (int i=k;i<2*k-1;i++)
            for (int j=1;j<=k;j++)
                h[i]=(h[i]+(LL)h[i-j]*f[j])%p;
        for (int i=0;i<k;i++)
            ans=(ans+(LL)res.a[i]*h[k+i-1])%p;
        printf("%d
    ",ans);
    }
    

      

  • 相关阅读:
    HDU-2602-Bone Collector
    HDU-1171-Big Event in HDU
    javascript概要
    核桃的数量
    P3372 【模板】线段树 1
    P3373 【模板】线段树 2
    拿糖果
    第二点五个不高兴的小明
    树的直径
    1240. 完全二叉树的权值
  • 原文地址:https://www.cnblogs.com/ezyzy/p/8286618.html
Copyright © 2011-2022 走看看