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

题目大意:

有数列:

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

求: hash=i=1mfi,nbasei h a s h = ∑ i = 1 m f i , n b a s e i
其中: n1e9,m200 n ≤ 1 e 9 , m ≤ 200 n3e6,m1e9 n ≤ 3 e 6 , m ≤ 1 e 9

解题思路:

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

主要是要求 m<n m < n Fm,n F m , n ,当 mn,fm,n m ≥ n , f m , n 都是 an a n ,求hash直接等比数列求和即可。

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

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

解得:

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

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

F=(axaxm+1)k=0(ax+(1a)xm+1)k F = ( a x − a x m + 1 ) ∑ k = 0 ∞ ( a x + ( 1 − a ) x m + 1 ) k

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

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

直接枚举 i i ,那么k=nim,根据调和级数求和,对所有 m<n m < n Gm,n G m , n 复杂度为 O(nlogn) O ( n l o g n )

完结撒花,贴上代码:

#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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值