Re.常系数齐次递推

前言

嗯   我之前的不知道多少天看这个的时候到底在干什么呢

为什么那么。。  可能大佬们太强的缘故

最后仔细想想思路那么的emmm 

不说了  要落泪了 

唔唔唔


 前置

多项式求逆

多项式除法/取模


常系数齐次递推目的

求一个满足k阶齐次线性递推数列ai的第n

即: 

给出f1--fk,a0--ak-1求an

N=1e9,K=32000


 常系数齐次递推主要思路

emmm矩阵快速幂怎么样都应该会的

设转移矩阵为A,st=[a0,a1...ak-2,ak-1]为初始矩阵

显然an=(st*An)0

O(k3logn)和O(k2logklogn)的矩阵快速幂在此范围下显然太暴力了

发现k过大时时间复杂度主要花在矩阵乘法上

考虑如何不用矩阵通过多项式来计算答案

先考虑把An转化为A0--Ak-1组合出来的和

设An=Q(A)*G(A)+R(A)

Q,G,R是以矩阵为x(参数)的多项式

当强制G的多项式的最高次数为k次方

那么可写成An=Q(A)*G(A)+ciAi

此时如果再强制试使得G(A)为0时

那么Q(A)*G(A)=0

An=ciAi=R(A)

所以ciAi=An%G(A)

通过多项式取模就可将An转化为ciAi

通过上面的推导发现an=(st*An)0=(st*ciAi)0=(ciAist)0

因为我们每次只取矩阵的第0项  每转移一次下一项就往上移一个位置 原来的第0项就去掉

所以Aist就等于sti

最后的an=cisti

这样只要找出之前要求的那个G(A)就可以O(k)得出答案了

那么如何求出G(A)

设G(A)=giAi=0

这里有个我暂时不会的结论

如果递推系数为f1--fn

那么gk-i=fi,gk=1

所以最后流程就是

1.求出G(A)

2.用快速幂和多项式取模求出An在模G(A)时的余数R(A) 也就是把An转化为A1--Ak的组合

3.计算答案an=cisti


代码

这 时隔多年我中于调出来了一份常数巨大的代码 

 #include<bits/stdc++.h> 
using namespace std;
#define ll long long
#define C getchar()-48
inline ll read()
{
    ll s=0,r=1;
    char c=C;
    for(;c<0||c>9;c=C) if(c==-3) r=-1;
    for(;c>=0&&c<=9;c=C) s=(s<<3)+(s<<1)+c;
    return s*r;
} 
const int p=998244353,G=3,N=140010;
int n,k,mx,cs,qvq,tz;
ll rev[N];
ll f[N],st[N],g[N],invg[N];
ll tmp[N],tmp1[N],tmp2[N],tmpa[N],tmpb[N];
ll a[N],ans[N];
inline ll ksm(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=(ans*a)%p;
        a=(a*a)%p;
        b>>=1;
    }
    return ans;
} 
inline void ntt(ll *a,ll n,ll kd)
{
    for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    for(int i=1;i<n;i<<=1)
    {
        ll gn=ksm(G,(p-1)/(i<<1));
        for(int j=0;j<n;j+=(i<<1))
        {
            ll t1,t2,g=1;
            for(int k=0;k<i;k++,g=g*gn%p)
            {
                t1=a[j+k],t2=g*a[j+k+i]%p;
                a[j+k]=(t1+t2)%p,a[j+k+i]=(t1-t2+p)%p;
            } 
        }
    }
    if(kd==1) return;
    ll ny=ksm(n,p-2);
    reverse(a+1,a+n);
    for(int i=0;i<n;i++) a[i]=a[i]*ny%p;
} 
inline void cl(ll *a,ll *b,ll n,ll m,ll len,ll w)
{
    for(int i=0;i<len;i++) tmp1[i]=i<n?a[i]:0;
    for(int i=0;i<len;i++) tmp2[i]=i<m?b[i]:0;
    for(int i=0;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(w-1));
}
inline void polyinv(ll *a,ll *b,ll ed)
{
    b[0]=ksm(a[0],p-2);
    for(int k=1,j=0;k<=(ed<<1);k<<=1,j++)
    {
        ll len=k<<1;
        cl(a,b,k,k,len,j+1);
        ntt(tmp1,len,1);ntt(tmp2,len,1);
        for(int i=0;i<len;i++) b[i]=tmp2[i]*(2ll-tmp1[i]*tmp2[i]%p+p)%p;
        ntt(b,len,-1);
        for(int i=k;i<len;i++) b[i]=0;
    }
}
inline void polymul(ll *a,ll *b,ll *c,ll n,ll m)
{
    ll len=1,w=0;
    while(len<=(n+m)) len<<=1,w++;
    cl(a,b,n,m,len,w);
    ntt(tmp1,len,1);ntt(tmp2,len,1);
    for(int i=0;i<len;i++) c[i]=tmp1[i]*tmp2[i]%p;
    ntt(c,len,-1);
}
inline void polymod(ll *a,ll n=mx<<1,ll m=k)
{                   
    int ed=(mx<<1);while(a[--ed]==0);if(ed<k) return; 
    
    n=ed;
    reverse(a,a+1+n);
    polymul(a,invg,tmpa,n+1,n-m+1);    
    reverse(tmpa,tmpa+n-m+1);
    reverse(a,a+1+n);
    
    polymul(g,tmpa,tmpb,m+1,n-m+1);
    for(int i=0;i<k;i++) a[i]=(a[i]-tmpb[i]+p)%p;
    for(int i=k;i<=ed;i++)a[i]=0;
    for(int i=0;i<(mx<<1);i++) tmpa[i]=tmpb[i]=0;
}
int main()
{
    n=read(),k=read();mx=1,cs=0;
    while(mx<=k) mx<<=1,cs++;
    for(int i=1;i<=k;i++) f[i]=read(),f[i]=f[i]<0?f[i]+p:f[i];
    for(int i=0;i<k;i++) st[i]=read(),st[i]=st[i]<0?st[i]+p:st[i];
    for(int i=1;i<=k;i++) g[k-i]=p-f[i];g[k]=1;
    for(int i=0;i<=k;i++) tmp[i]=g[i];
    
    reverse(tmp,tmp+1+k);
    polyinv(tmp,invg,mx);
    for(int i=mx;i<=(mx<<1);i++) invg[i]=0;
    for(int i=0;i<=k;i++) tmp[i]=0;
    for(int i=0;i<(mx<<1);i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(cs+1-1));
    ans[0]=1;a[1]=1;
    while(n)
    {
        if(n&1){polymul(ans,a,ans,k,k); polymod(ans);}
        polymul(a,a,a,k,k); polymod(a);
        n>>=1;
    }
    for(int i=0;i<k;i++) qvq=(qvq+ans[i]*st[i])%p;
    cout<<qvq; 
    return 0;
}
原文地址:https://www.cnblogs.com/1436177712qqcom/p/10497930.html