常系数齐次线性递推

矩阵乘法可以被优化成$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} \rfloor} \times M^{n- \lfloor \frac{n}{2} \rfloor}$,做多项式乘法,得到一个$2k-2$次的多项式,然后不停往前推,直到剩余一个$k-1$次多项式。

最后得到$XM^n=X\sum_{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\n",ans);
}

  

posted @ 2018-01-15 09:02  SD_le  阅读(1108)  评论(0编辑  收藏  举报
重置按钮