Description
“简单无向图”是指无重边、无自环的无向图(不一定连通)。
一个带标号的图的价值定义为每个点度数的k次方的和。
给定n和k,请计算所有n个点的带标号的简单无向图的价值之和。
因为答案很大,请对
998244353
998244353
998244353取模输出。
第一行包含两个正整数 n , k ( 1 ≤ n ≤ 1 0 9 , 1 ≤ k ≤ 200000 ) n,k(1\le n\le 10^9,1\le k\le 200000) n,k(1≤n≤109,1≤k≤200000)
Solution
注意到每个点的贡献都是一样的,我们只需要枚举每个点的度数单独考虑就行了
a
n
s
=
n
⋅
2
(
n
−
1
2
)
∑
i
=
0
n
(
n
−
1
i
)
ans=n\cdot{2}^{\binom{n-1}{2}}\sum\limits_{i=0}^{n}\binom{n-1}{i}
ans=n⋅2(2n−1)i=0∑n(in−1)
然后就是非常套路的
i
k
=
∑
j
=
0
k
{
k
j
}
(
i
j
)
j
!
i^k=\sum\limits_{j=0}^k \begin{Bmatrix}k\\j\end{Bmatrix}\binom{i}{j}j!
ik=j=0∑k{kj}(ji)j!
带进去就是
a
n
s
=
n
⋅
2
(
n
−
1
2
)
∑
i
=
0
n
−
1
(
n
−
1
i
)
∑
j
=
0
i
(
i
j
)
{
k
j
}
j
!
ans=n\cdot{2}^{\binom{n-1}{2}}\sum\limits_{i=0}^{n-1}\binom{n-1}{i}\sum\limits_{j=0}^{i}\binom{i}{j}\begin{Bmatrix}k\\j\end{Bmatrix}j!
ans=n⋅2(2n−1)i=0∑n−1(in−1)j=0∑i(ji){kj}j!
交换枚举顺序 a n s = n ⋅ 2 ( n − 1 2 ) ∑ j = 0 n − 1 { k j } j ! ∑ i = j n − 1 ( n − 1 i ) ( i j ) ans=n\cdot{2}^{\binom{n-1}{2}}\sum\limits_{j=0}^{n-1}\begin{Bmatrix}k\\j\end{Bmatrix}j!\sum\limits_{i=j}^{n-1}\binom{n-1}{i}\binom{i}{j} ans=n⋅2(2n−1)j=0∑n−1{kj}j!i=j∑n−1(in−1)(ji)
然后有等柿 ∑ j = 0 n − 1 ∑ i = j n − 1 ( n − 1 i ) ( i j ) = ∑ j = 0 n − 1 ∑ i = j n − 1 ( n − 1 j ) ( n − j − 1 i − j ) = ∑ j = 0 n − 1 ( n − 1 j ) ∑ i = j n − 1 ( n − j − 1 i − j ) = ∑ j = 0 n − 1 ( n − 1 j ) 2 n − j − 1 \sum\limits_{j=0}^{n-1}\sum\limits_{i=j}^{n-1}\binom{n-1}{i}\binom{i}{j}=\sum\limits_{j=0}^{n-1}\sum\limits_{i=j}^{n-1}\binom{n-1}{j}\binom{n-j-1}{i-j}=\sum\limits_{j=0}^{n-1}\binom{n-1}{j}\sum\limits_{i=j}^{n-1}\binom{n-j-1}{i-j}=\sum\limits_{j=0}^{n-1}\binom{n-1}{j}2^{n-j-1} j=0∑n−1i=j∑n−1(in−1)(ji)=j=0∑n−1i=j∑n−1(jn−1)(i−jn−j−1)=j=0∑n−1(jn−1)i=j∑n−1(i−jn−j−1)=j=0∑n−1(jn−1)2n−j−1
为什么呢?两个组合数相乘的意义等价于从 n − 1 n-1 n−1个中直接取出 j j j个,剩余 i − j i-j i−j个随便选。或者直接化也可以
于是我们的答案可以这么算 a n s = n ⋅ 2 ( n − 1 2 ) ∑ i = 0 n − 1 2 n − i − 1 { k i } ( n − 1 i ) i ! ans=n\cdot 2^{\binom{n-1}{2}}\sum\limits_{i=0}^{n-1}2^{n-i-1}\begin{Bmatrix}k\\i\end{Bmatrix}\binom{n-1}{i}i! ans=n⋅2(2n−1)i=0∑n−12n−i−1{ki}(in−1)i!
注意到 k > i k>i k>i的时候斯特林数为 0 0 0,因此只需要算到 m i n ( n , k ) min(n,k) min(n,k)即可
斯特林数用容斥柿子NTT算,预处理阶乘和逆元就可以惹
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
typedef long long LL;
const int MOD=998244353;
const int ny2=499122177;
const int N=1048580;
LL fac[N],rev[N],s[N],ny[N];
LL ksm(LL x,LL dep) {
LL res=1;
for (;dep;dep>>=1) {
(dep&1)?(res=res*x%MOD):0;
x=x*x%MOD;
}
return res;
}
LL mod(LL x) {
(x>=MOD)?(x-=MOD):0;
(x<0)?(x+=MOD):0;
return x;
}
void NTT(LL *a,int n,int f) {
for (int i=0;i<n;++i) if (i<rev[i]) std:: swap(a[i],a[rev[i]]);
for (int i=1;i<n;i<<=1) {
LL wn=0;
if (f==1) wn=ksm(3,(MOD-1)/i/2);
else wn=ksm(3,(MOD-1)-(MOD-1)/i/2);
for (int j=0;j<n;j+=(i<<1)) {
LL w=1;
for (int k=0;k<i;++k) {
LL u=a[j+k],v=a[j+k+i]*w%MOD;
a[j+k]=mod(u+v); a[j+k+i]=mod(u-v);
w=w*wn%MOD;
}
}
}
if (f==-1) {
LL ny=ksm(n,MOD-2);
for (int i=0;i<n;++i) a[i]=ny*a[i]%MOD;
}
}
int n,m;
void pre() {
scanf("%d%d",&n,&m);
fac[0]=ny[0]=1; for(int i=1;i<N;++i) {
fac[i]=fac[i-1]*i%MOD;
}
ny[N-1]=ksm(fac[N-1],MOD-2);
for (int i=N-2;i>=0;--i) {
ny[i]=ny[i+1]*(i+1)%MOD;
}
static LL b[N];
int len=1,lg=0; for (;len<=m*2;len<<=1,++lg);
for (int i=0;i<len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
for (int i=0;i<=m;++i) {
s[i]=ny[i];
if (i&1) s[i]=MOD-s[i];
b[i]=ksm(i,m)*ny[i]%MOD;
}
NTT(s,len,1); NTT(b,len,1);
for (int i=0;i<len;++i) s[i]=s[i]*b[i]%MOD;
NTT(s,len,-1);
}
int main(void) {
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
pre();
LL ans=0,lxf=1;
for (int i=0,lim=std:: min(n-1,m);i<=lim;++i) {
LL tmp=ksm(2,n-i-1)*s[i]%MOD*lxf%MOD*ny[i]%MOD*fac[i]%MOD;
ans=mod(ans+tmp);
lxf=lxf*(n-i-1)%MOD;
}
ans=n*ksm(2,1LL*(n-1)*(n-2)/2)%MOD*ans%MOD;
printf("%lld\n", ans);
return 0;
}