BZOJ5093 图的价值(NTT+斯特林数)

显然每个点会提供相同的贡献。于是现在只考虑1号点的贡献。若其度数为i,则在2~n号点选i个连上,剩下的边随便连,这样可以算出答案为

这个式子可以O(n)计算。发现k比较小,于是考虑如何将这个式子化为与k有关的求和。

显然前面一部分可以直接提走。考虑后面一部分的组合意义:n-1个有标号盒子里面选i个,放进去k个球的方案数

可以对这个过程进行变换:把k个球放在n-1个有标号盒子里,有球的盒子必须选,没有的可选可不选的方案数

枚举有球的盒子有多少个,可以发现答案变成了一个与k有关的式子:

S(k,i)为第二类斯特林数,也即将k个小球放进i个盒子(每个盒子非空)的方案数。

问题变为快速求斯特林数。可以用容斥原理推导出斯特林数卷积形式的通项公式:

即给盒子标上号,枚举有几个空盒。再化一下:

这样卷积形式就很明显了。用NTT算一下即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define P 998244353
#define N 300000
int n,k,a[N],v[N<<1],s[N<<1],inv[N],ans,ans2;
int t,r[N<<1];
int ksm(int a,int k)
{
    if (k==0) return 1;
    int tmp=ksm(a,k>>1);
    if (k&1) return 1ll*tmp*tmp%P*a%P;
    else return 1ll*tmp*tmp%P;
}
void inc(int &x,int y){x+=y;if (x>=P) x-=P;}
void DFT(int n,int *a,int p)
{
    for (int i=0;i<n;i++) if (i<r[i]) swap(a[i],a[r[i]]);
    for (int i=2;i<=n;i<<=1)
    {
        int wn=ksm(p,(P-1)/i);
        for (int j=0;j<n;j+=i)
        {
            int w=1;
            for (int k=j;k<j+(i>>1);k++,w=1ll*w*wn%P)
            {
                int x=a[k],y=1ll*w*a[k+(i>>1)]%P;
                a[k]=(x+y)%P;a[k+(i>>1)]=(x-y+P)%P;
            }
        }
    }
}
int main()
{
    freopen("bzoj5093.in","r",stdin);
    freopen("bzoj5093.out","w",stdout);
    n=read(),k=read();
    ans=1ll*n*ksm(2,1ll*(n-1)*(n-2)/2%(P-1))%P;
    n--;
    inv[1]=1;
    for (int i=2;i<=max(3,min(n,k));i++) inv[i]=(P-1ll*(P/i)*inv[P%i]%P)%P;
    a[0]=ksm(2,n);
    for (int i=1;i<=min(n,k);i++)
    a[i]=1ll*a[i-1]*inv[2]%P*(n-i+1)%P;
    v[0]=1;
    for (int i=1;i<=min(n,k);i++)
    v[i]=(P-1ll*v[i-1]*inv[i]%P)%P;
    s[0]=0;int facinv=1;
    for (int i=1;i<=min(n,k);i++)
    {
        facinv=1ll*facinv*inv[i]%P;
        s[i]=1ll*ksm(i,k)*facinv%P; 
    }
    t=1;while (t<=(min(n,k)<<1)) t<<=1;
    for (int i=0;i<t;i++) r[i]=(r[i>>1]>>1)|(i&1)*(t>>1);
    DFT(t,s,3),DFT(t,v,3);
    for (int i=0;i<t;i++) s[i]=1ll*s[i]*v[i]%P;
    DFT(t,s,inv[3]);
    int p=ksm(t,P-2);
    for (int i=0;i<t;i++) s[i]=1ll*s[i]*p%P;
    for (int i=0;i<=min(n,k);i++)
    inc(ans2,1ll*a[i]*s[i]%P);
    ans=1ll*ans*ans2%P;
    cout<<ans;
    fclose(stdin);fclose(stdout);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Gloid/p/9390082.html
今日推荐