[BZOJ4555]求和 多项式求逆

博客探讨了求和问题,涉及将n个元素分配到i个集合的组合计数,其中集合有两种状态。通过组合数学展开求解gn的表达式,并讨论其与gn-i的关系。

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

gn=ni=0{ni}2ii! g n = ∑ i = 0 n { n i } 2 i i ! ,题目即求 ni=0gi ∑ i = 0 n g i
考虑 gn g n 的组合意义,把 n n 个元素放进i个集合,这些集合有顺序之分,而且每个集合有两种状态,那么考虑枚举一个集合, gn=ni=12(ni)gni g n = ∑ i = 1 n 2 ( n i ) g n − i
展开组合数:

gnn!=i=1n2i!gni(ni)! g n n ! = ∑ i = 1 n 2 i ! g n − i ( n − i ) !

G(x)=i=0gii!xi,F(x)=i=12i!xi G ( x ) = ∑ i = 0 ∞ g i i ! x i , F ( x ) = ∑ i = 1 ∞ 2 i ! x i ,那么有:
G(x)=G(x)F(x)+1G(x)=11F(x) G ( x ) = G ( x ) ∗ F ( x ) + 1 ⇒ G ( x ) = 1 1 − F ( x )

+1 + 1 是因为 F(x) F ( x ) 是从 1 1 开始卷积的,要补上G(x)的常数项。
多项式求逆即可。
代码:

#include<iostream>
#include<cstdio>
#define ll long long
#define N 262150
using namespace std;
const int mod=998244353;
int n,m;
ll fac[N],ifac[N],f[N],g[N],c[N],r[N],ta[N],tb[N];
ll ksm(ll a,int b){ll r=1; for(b=(b+mod-1)%(mod-1);b;b>>=1){if(b&1)r=r*a%mod;a=a*a%mod;}return r;}
void ntt(ll *a,int n,int f)
{
    for(int i=0;i<n;i++)
        r[i]=(r[i>>1]>>1)|((i&1)*(n>>1));
    for(int i=0;i<n;i++)
        if(i<r[i]) swap(a[i],a[r[i]]);
    for(int i=1;i<n;i<<=1)
    {
        ll wn=ksm(3,(mod-1)*f/(i<<1));
        for(int j=0;j<n;j+=(i<<1))
        {
            ll wk=1;
            for(int k=j;k<j+i;k++,wk=wk*wn%mod)
            {
                ll x=a[k],y=wk*a[k+i]%mod;
                a[k]=(x+y)%mod;a[k+i]=(x-y+mod)%mod;
            }
        }
    }   
    if(f==-1) for(int i=0,tmp=ksm(n,mod-2);i<n;i++) a[i]=a[i]*tmp%mod;
}
void getinv(ll *a,ll *b,int n)
{
    if(n==0){b[0]=ksm(a[0],mod-2);return ;}
    getinv(a,b,n>>1);n<<=1;
    for(int i=0;i<n;i++)
        r[i]=(r[i>>1]>>1)|((i&1)*(n>>1)); 
    for(int i=0;i<(n>>1);i++)
        c[i]=a[i],c[i+(n>>1)]=0;
    ntt(c,n,1);ntt(b,n,1);
    for(int i=0;i<n;i++)
        b[i]=((b[i]<<1)-c[i]*b[i]%mod*b[i]%mod+mod)%mod;
    ntt(b,n,-1);        
    for(int i=(n>>1);i<n;i++) b[i]=0;  
}
int main()
{   
    scanf("%d",&m);
    for(n=1;n<=m;n<<=1);
    fac[0]=1;
    for(int i=1;i<=n;i++)
        fac[i]=fac[i-1]*i%mod;
    ifac[n]=ksm(fac[n],mod-2);
    for(int i=n-1;i>=0;i--)
        ifac[i]=ifac[i+1]*(i+1)%mod;    
    f[0]=1;
    for(int i=1;i<n;i++)
        f[i]=((mod-ifac[i])<<1)%mod;

    getinv(f,g,n);
    ll ans=0;
    for(int i=0;i<=m;i++)
        ans=(ans+g[i]*fac[i])%mod;
    printf("%lld",ans);         
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值