bzoj 5093: 图的价值 NTT+数学+第二类斯特林数

题意

“简单无向图”是指无重边、无自环的无向图(不一定连通)。
一个带标号的图的价值定义为每个点度数的k次方的和。
给定n和k,请计算所有n个点的带标号的简单无向图的价值之和。
因为答案很大,请对998244353取模输出。
1<=n<=10^9,1<=k<=200000

分析

枚举每个点和每个点连出去的边的数量,总共有n-1个点可以连,然后剩下的边连不连都可以。不难分析出

ans=n2(n1)(n2)2i=0n1Cin1ik

现在要求的就是
i=0nCinik

有一个结论就是
nk=i=0nS(k,i)Cini!

其中 S(k,i) 是第二类斯特林数。
那么有
i=0nCinik

=i=0nCinj=0iS(k,j)Cjij!

=j=0nS(k,j)j!i=jnCinCji

其中 i=jnCinCji 的组合意义是从n个数里面选i个数,再从这i个数里面选j个数,等价于从n个数里面选j个数然后其他的数选或不选都行,也就是 Cjn2nj
所以接着往下推就是
=j=0nS(k,j)j!Cjn2nj

考虑到用容斥原理得出的第二类斯特林数的卷积形式
S(n,m)=1m!i=0m(1)iCim(mi)n=i=0m(1)ii!(mi)n(mi)!

那么我们就可以用NTT把第二类斯特林数求出来,然后预处理组合数来算贡献即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;

const int N=200005;
const int MOD=998244353;

int n,k,jc[N],ny[N],pow[N],rev[N*4],s[N*4],s1[N*4],L,nyL,lg,jcn[N];

int ksm(int x,int y)
{
    int ans=1;
    while (y)
    {
        if (y&1) ans=(LL)ans*x%MOD;
        x=(LL)x*x%MOD;y>>=1;
    }
    return ans;
}

void NTT(int *a,int f)
{
    for (int i=0;i<L;i++) if (i<rev[i]) swap(a[i],a[rev[i]]);
    for (int i=1;i<L;i<<=1)
    {
        int wn=ksm(3,f==1?(MOD-1)/i/2:MOD-1-(MOD-1)/i/2);
        for (int j=0;j<L;j+=(i<<1))
        {
            int w=1;
            for (int k=0;k<i;k++)
            {
                int u=a[j+k],v=(LL)a[j+k+i]*w%MOD;
                a[j+k]=(u+v)%MOD;a[j+k+i]=(u-v)%MOD;
                w=(LL)w*wn%MOD;
            }
        }
    }
    if (f==-1) for (int i=0;i<L;i++) a[i]=(LL)a[i]*nyL%MOD;
}

int main()
{
    scanf("%d%d",&n,&k);n--;
    jc[0]=ny[0]=jc[1]=ny[1]=pow[0]=1;pow[1]=2;
    for (int i=2;i<=k;i++) jc[i]=(LL)jc[i-1]*i%MOD,ny[i]=(LL)(MOD-MOD/i)*ny[MOD%i]%MOD,pow[i]=pow[i-1]*2%MOD;
    for (int i=2;i<=k;i++) ny[i]=(LL)ny[i]*ny[i-1]%MOD;
    for (int i=0;i<=k;i++) s[i]=((i&1)?-1:1)*ny[i],s1[i]=(LL)ksm(i,k)*ny[i]%MOD;
    jcn[0]=1;
    for (int i=1;i<=k;i++) jcn[i]=(LL)jcn[i-1]*(n-i+1)%MOD;
    for (L=1;L<=k*2;L<<=1,lg++);nyL=ksm(L,MOD-2);
    for (int i=0;i<L;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
    NTT(s,1);NTT(s1,1);
    for (int i=0;i<L;i++) s[i]=(LL)s[i]*s1[i]%MOD;
    NTT(s,-1);
    int ans=0;
    for (int i=0;i<=min(n,k);i++) (ans+=(LL)s[i]*jc[i]%MOD*jcn[i]%MOD*ny[i]%MOD*ksm(2,n-i)%MOD)%=MOD;
    ans=(LL)ans*(n+1)%MOD*ksm(2,(LL)n*(n-1)/2%(MOD-1))%MOD;
    ans+=ans<0?MOD:0;
    printf("%d",ans);
    return 0;
}
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值