图的价值
Description
“简单无向图”是指无重边、无自环的无向图(不一定连通)。
一个带标号的图的价值定义为每个点度数的k次方的和。
给定n和k,请计算所有n个点的带标号的简单无向图的价值之和。
因为答案很大,请对998244353取模输出。
Input
第一行包含两个正整数n,k(1<=n<=10^9,1<=k<=200000)。
Output
输出一行一个整数,即答案对998244353取模的结果。
Sample Input
6 5
Sample Output
67584000
来练习stirling数~
然后进入天坑
思路:
首先考虑固定一个点,计算这个点的贡献。
考虑到这个点可以与其余的
n−1
个点连边,且其他
n−1
个点之间的连边并不需要去关心,那么有:
ansi=2(n−1)(n−2)2∗∑i=0n−1(n−1i)∗ik
于是
ans=n∗2(n−1)(n−2)2∗∑i=0n−1(n−1i)∗ik
前半部分显然可以快速计算,那么考虑后半部分。
现在咱们的目标就变成了化简如下式子
∑i=0n−1(n−1i)∗ik
首先使用拆开
xk
的方法:
xk=∑i=1k{ki}∗(xi)∗i!
具体原理详见 另一篇博客,这道题的第一步便是推导出这个式子~
那么原式变成了
∑i=0n−1(n−1i)∗∑j=1k{kj}∗(ij)∗j!
移项化简:
∑j=1k{kj}∗j!∗∑i=jn−1(n−1i)(ij)
考虑
∑i=jn−1(n−1i)(ij)
的组合意义,相当于从
n−1
个物体里选择
i
个,再从选出的
那么原式变为:
∑j=1k{kj}∗j!∗(n−1j)∗2n−j−1
然后可以发现剩下的式子的唯一瓶颈便是stirling数的计算。
考虑stirling数的公式:
{nm}=1m!∑k=0m(−1)k∗(mk)∗(m−k)n
拆开组合数:
{nm}=1m!∑k=0m(−1)k∗m!k!(m−k)!∗(m−k)n
{nm}=∑k=0m(−1)k∗1k!(m−k)!∗(m−k)n
向卷积的形式靠拢:
{nm}=∑k=0m(−1)kk!∗(m−k)n(m−k)!
哇然后咱们可以卷积!
NTT直接上!
于是成功解决此问题!
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
const int K=200009;
const ll md=998244353;
ll n,k,m,l;
ll fac[K],inv[K],ifac[K];
ll s[K<<2],a[K<<2],b[K<<2];
int rev[K<<2];
inline ll qpow(ll a,ll b)
{
ll ret=1;
while(b)
{
if(b&1)
ret=ret*a%md;
a=a*a%md;
b>>=1;
}
return ret;
}
inline void init()
{
fac[0]=1;
for(ll i=1;i<K;i++)
fac[i]=fac[i-1]*i%md;
inv[K-1]=qpow(fac[K-1],md-2);
for(ll i=K-1;i>=1;i--)
inv[i-1]=inv[i]*i%md;
ifac[0]=1;
for(ll i=1;i<=k;i++)
ifac[i]=ifac[i-1]*(n-i)%md;
}
inline ll c(ll a,ll b)
{
return ifac[b]*inv[b]%md;
}
inline void NTT(ll *a,int n,bool f)
{
for(int i=0;i<n;i++)
if(rev[i]>i)
swap(a[i],a[rev[i]]);
for(int h=2;h<=n;h<<=1)
{
ll wn=qpow(3ll,(md-1)/h);
if(f)wn=qpow(wn,md-2);
for(int j=0;j<n;j+=h)
{
ll w=1ll;
for(int k=j;k<j+(h>>1);k++)
{
ll x=a[k],y=w*a[k+(h>>1)]%md;
a[k]=(x+y)%md;
a[k+(h>>1)]=(x-y+md)%md;
w=w*wn%md;
}
}
}
if(f)
for(ll i=0,invn=qpow(n,md-2);i<n;i++)
a[i]=a[i]*invn%md;
}
int main()
{
scanf("%lld%lld",&n,&k);
init();
ll ans=0;
for(int i=0;i<=k;i++)
{
a[i]=((i&1)?(md-1ll):(1ll))*inv[i]%md;
b[i]=qpow(i,k)*inv[i]%md;
}
for(m=1;m<=(k<<1);m<<=1)l++;
for(int i=0;i<m;i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
NTT(a,m,0);NTT(b,m,0);
for(int i=0;i<m;i++)
s[i]=a[i]*b[i]%md;
NTT(s,m,1);
for(int j=0;j<=k;j++)
(ans+=s[j]*fac[j]%md*c(n-1,j)%md*qpow(2,n-j-1)%md)%=md;
ans=ans*n%md*qpow(2,(n-1)*(n-2)/2)%md;
printf("%lld\n",ans);
return 0;
}