常系数齐次线性递推

题意:

$h(i)=\sum_{j=1}^{k} {h(i-j)*f(j)}$

给出$h(0..k-1)$ 求$h(n)$

题解:

学了无限久。。

首先矩阵快速幂求复杂度是$logn*k^3$的

因为转移矩阵具有一些特性,所以可以优化复杂度

所以这个算法也只能对于线性递推

$$f(\alpha)=det(\alpha*I-M)$$ 这个东西是它的特征多项式

然后去计算这个东西的行列式(可以用数学归纳法)

得到$$f(\alpha)=\alpha^k-\sum_{i=1}^{k} {M[i]* \alpha^{k-i}}$$

而这个东西叫做它的特征值

然后根据Cayley-Hamilton定理  矩阵的特征多项式是它的零化多项式

而矩阵的零化多项式满足$M'=0$ 即$f(M)=0$

于是我们可以把当前多项式对$f(M)$取模

然后我们等价于求

$$x^n \% (x^k-\sum_{i=0}^{k-1}{f[k-i]*x^i} )$$

取模的话就是

dep(i,2*k-2,k)
      rep(j,1,k)
        z.a[i-j]=(z.a[i-j]+1ll*z.a[i]*f[j])%mo;

因为第一项为1,后面的为负号,所以就直接这么加了

这样一次取模是$k^2$的 可以利用fft优化多项式取模优化到$klogk$

这样我们可以得到$M^n=\sum_{i=0}^{k-1} {M^i*c[i]}$

关键在于如何快速求这个式子

如果求完整的那么复杂度反而更高了

我们注意到我们只需要$a[n]$这项

所以我们两边同乘$vo$ $$vo*M^n=\sum_{i=0}^{k-1} {vo*M^i*c[i]}$$

而$vo*M^k$的第一项为$a[k]$ (网上有代码用了最后一项这样写起来会麻烦一点而且得特判n<k)

所以就是$$a[n]=\sum_{i=0}^{k-1} {a[i]*c[i]}$$

这样就可以在$logn*k^2$内解决了

代码:

#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define IL inline
#define rep(i,h,t) for(int i=h;i<=t;i++)
#define dep(i,t,h) for(int i=t;i>=h;i--)
#define ll long long
#define me(x) memset(x,0,sizeof(x))
#define mep(x,y) memcpy(x,y,sizeof(y))
#define mid ((h+t+1)>>1)
namespace IO{
    char ss[1<<24],*A=ss,*B=ss;
    IL char gc()
    {
        return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++;
    }
    template<class T> void read(T &x)
    {
        rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48);
        while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; 
    }
    char sr[1<<24],z[20]; int Z,C1=-1;
    template<class T>void wer(T x)
    {
        if (x<0) sr[++C1]='-',x=-x;
        while (z[++Z]=x%10+48,x/=10);
        while (sr[++C1]=z[Z],--Z);
    }
    IL void wer1()
    {
        sr[++C1]=' ';
    }
    IL void wer2()
    {
        sr[++C1]='\n';
    }
    template<class T>IL void maxa(T &x,T y) {if (x<y) x=y;}
    template<class T>IL void mina(T &x,T y) {if (x>y) x=y;} 
    template<class T>IL T MAX(T x,T y){return x>y?x:y;}
    template<class T>IL T MIN(T x,T y){return x<y?x:y;}
};
using namespace IO;
const int mo=1e9+7;
const int N=4010;
int f[N],h[N],n,k;
struct re{
    int a[N];
    re() {me(a);}
}a;
re js(re x,re y)
{
    re z;
    rep(i,0,k-1)
      rep(j,0,k-1)
        z.a[i+j]=(z.a[i+j]+1ll*x.a[i]*y.a[j])%mo;
    dep(i,2*k-2,k)
      rep(j,1,k)
        z.a[i-j]=(z.a[i-j]+1ll*z.a[i]*f[j])%mo;
    return z;
}
re ksm(int x)
{
    if (x==1) return(a);
    re now=ksm(x/2);
    now=js(now,now);
    if (x%2==1) now=js(now,a);
    return now;
}
int main()
{
    //转移矩阵为f 初始为h
    freopen("1.in","r",stdin);
//    freopen("1.out","w",stdout);
    read(n); read(k);
    rep(i,1,k) 
    {
      read(f[i]);
      if (f[i]<0) f[i]+=mo;
    }
    rep(i,0,k-1)
    {
        read(h[i]);
        if (h[i]<0) h[i]+=mo;
    }
    a.a[1]=1;
    re now=ksm(n);
    ll ans=0;
    rep(i,0,k-1)
      ans=(ans+1ll*now.a[i]*h[i])%mo; 
   /* re now=ksm(n-k+1);
    rep(i,k,2*k-1)
      rep(j,1,k)
        h[i]=(h[i]+1ll*h[i-j]*f[j])%mo;
    ll ans=0;
    rep(i,0,k-1)
      ans=(ans+1ll*now.a[i]*h[k+i-1])%mo; */ 
    cout<<ans<<endl;
    return 0;
}

 

posted @ 2018-12-03 11:10  尹吴潇  阅读(163)  评论(0编辑  收藏  举报