BJ模拟 数列【特征多项式and生成函数】

本文介绍了一种针对特定数列求和问题的高效算法,通过特征多项式优化常系数线性递推,并利用生成函数及泰勒展开进行进一步优化,最终实现O(nlogn)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

有数列:

fm,n=an,n=1...mk=1m(a1)fm,nk1(1)(1)fm,n={an,n=1...m∑k=1m(a−1)fm,n−k−1

求:hash=i=1mfi,nbaseihash=∑i=1mfi,nbasei
其中:n1e9,m200n≤1e9,m≤200n3e6,m1e9n≤3e6,m≤1e9

解题思路:

前一部分可以用特征多项式优化常系数线性递推做,主要是第二部分。

主要是要求m<nm<nFm,nFm,n,当mn,fm,nm≥n,fm,n都是anan,求hash直接等比数列求和即可。

注意如果把非正数项都看作0,除了fm,1fm,m+1fm,1、fm,m+1,都有fm,n=afm,n1(a1)fnm1,mfm,n=afm,n−1−(a−1)fn−m−1,m

考虑fmfm的生成函数Fm(x)=k=0fm,iFm(x)=∑k=0∞fm,i,根据上述特点,有:
F=axF(a1)xm+1F+axaxm+1F=axF−(a−1)xm+1F+ax−axm+1(第1项少算了a,第m+1项多算了a,要特殊考虑)。

解得:

F=axaxm+11ax+(a1)xm+1F=ax−axm+11−ax+(a−1)xm+1

由泰勒展开11x=1+x+x2+x3+...11−x=1+x+x2+x3+...,所以有:

F=(axaxm+1)k=0(ax+(1a)xm+1)kF=(ax−axm+1)∑k=0∞(ax+(1−a)xm+1)k

不妨设G=k=0(ax+(1a)xm+1)kG=∑k=0∞(ax+(1−a)xm+1)k,那么F=(axaxm+1)GF=(ax−axm+1)G
所以FF的n次方系数Fn=a(Gn1Gnm1)

所以考虑如何求GG的n次方系数Gn
GG用二项式展开:G=k=0i=0k(ki)aki(1a)ixim+k

直接枚举ii,那么k=nim,根据调和级数求和,对所有m<nm<nGm,nGm,n复杂度为O(nlogn)O(nlogn)

完结撒花,贴上代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=3000005,M=505,mod=998244353,base=19260817;
int n,a;
struct Poly
{
    ll a[M];int deg;
    Poly(){memset(a,0,sizeof(a));}
    inline friend Poly mul(const Poly &A,const Poly &B,const Poly &C)
    {
        Poly res;res.deg=A.deg+B.deg;
        for(int i=0;i<=A.deg;i++)
            for(int j=0;j<=B.deg;j++)
                res.a[i+j]=(res.a[i+j]+A.a[i]*B.a[j])%mod;
        for(int i=res.deg;i>=C.deg;i--)
            for(int j=0;j<=C.deg;j++)
                res.a[i-C.deg+j]=(res.a[i-C.deg+j]-res.a[i]*C.a[j])%mod;
        res.deg=C.deg;
        return res;
    }
    inline friend Poly Pow(Poly A,int b,const Poly &C)
    {
        Poly res;res.a[0]=1,res.deg=C.deg;
        for(;b;b>>=1,A=mul(A,A,C))
            if(b&1)res=mul(res,A,C);
        return res;
    }
};

int calc(int m)
{
    Poly A,B,C;
    A.a[0]=B.a[1]=C.a[m]=1;
    A.deg=B.deg=C.deg=m;
    for(int i=1;i<=m;i++)A.a[i]=A.a[i-1]*a%mod;
    if(n<=m)return A.a[n];
    for(int i=0;i<m;i++)C.a[i]=1-a;
    B=Pow(B,n-1,C);
    int res=0;
    for(int i=0;i<m;i++)res=(res+B.a[i]*A.a[i+1])%mod;
    return (res+mod)%mod;
}

void solve1(int m)
{
    ll ans=0,bs=1,g;
    for(int i=1;i<=m;i++)
    {
        bs=bs*base%mod;
        g=calc(i);
        ans=(ans+g*bs)%mod;
    }
    printf("%d\n",ans);
}

ll fac[N],fac_inv[N],bin1[N],bin2[N];

int Pow(ll x,int y)
{
    ll res=1;
    for(;y;y>>=1,x=x*x%mod)
        if(y&1)res=res*x%mod;
    return res;
}

int C(int x,int y){return fac[x]*fac_inv[y]%mod*fac_inv[x-y]%mod;}

int G(int m,int n)
{
    ll res=0;
    for(int i=0;i*m<=n;i++)
    {
        int k=n-i*m;
        if(k<i)continue;
        res=(res+bin1[k-i]*bin2[i]%mod*C(k,i))%mod;
    }
    return res;
}

void solve2(int m)
{
    ll g,bs=1,ans=0;
    fac[0]=bin1[0]=bin2[0]=1;
    for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
    for(int i=1;i<=n;i++)bin1[i]=bin1[i-1]*a%mod;
    for(int i=1;i<=n;i++)bin2[i]=bin2[i-1]*(1-a+mod)%mod;
    fac_inv[n]=Pow(fac[n],mod-2);
    for(int i=n-1;i>=0;i--)fac_inv[i]=fac_inv[i+1]*(i+1)%mod;
    for(int i=1;i<n&&i<=m;i++)
    {
        bs=bs*base%mod,g=(ll)a*(G(i,n-1)-G(i,n-i-1)+mod)%mod;
        ans=(ans+g*bs)%mod;
    }
    if(n<=m)
    {
        bs=bs*base%mod;
        g=bs*(Pow(base,m-n+1)-1)%mod*Pow(base-1,mod-2)%mod;
        ans=(ans+bin1[n]*g)%mod;
    }
    printf("%d\n",ans);
}

int main()
{
    //freopen("lx.in","r",stdin);
    int m=getint();a=getint(),n=getint();
    if(m<=200)solve1(m);
    else solve2(m);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值