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

    题意:

    $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]='
    ';
        }
        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;
    }
  • 相关阅读:
    SQL Server检测是不是数字类型的函数(非ISNUMERIC)
    网页中的QQ和阿里旺旺聊天图标
    转载:用 document.readyState == "complete" 判断页面是否加载完成
    sql server行转列 列转行交叉表实现[转]
    拆分列值
    值得收藏的各类型数据库连接串事例网站
    保留几个图表生成免费资源
    MSN消息提示类(II)
    生成Excel”服务器进程80070005“错误和“异常来自 HRESULT:0x800A03EC”的错误,windows server 2008 32位和64位下的特殊设置。
    winform中DataGridView的某些属性
  • 原文地址:https://www.cnblogs.com/yinwuxiao/p/10057341.html
Copyright © 2011-2022 走看看