公式恐惧症慎入。
题目要求边权在
1∼M 之间,我们转化一下,变成
0∼M−1 之间,最后再将答案加上
(N−1)×M2N(N−1) 就行了。
对于一个图
G,设
Gk 是由
G 中边权
小于 k 的边组成的图,而
c(Gk) 是
Gk 中连通块的个数,那么其最小生成树的边权和为
∑k=1M(c(Gk)−1)。
为什么?
如果一条边权为
v 边在最小生成树内,那么只有枚举的
k 在
v+1∼M 的时候才会有这条边,换句话说,也就是才会让联通块数减一。
那么,在这之前,
k 从
1∼v 共
v 次枚举时联通块数会多一,总共就正好加上了
v。
原式又可化简为
−M+∑k=1Mc(Gk)。
设
S 是所有
M2N(N+1) 个完全图的集合,
C(Gk) 是
Gk 的连通块的集合,那么总答案为:
==(N−1)×M2N(N−1)+G∈S∑(−M+k=1∑Mc(Gk))(N−1)×M2N(N−1)−M×M2N(N−1)+k=1∑MG∈S∑c(Gk)(N−1−M)×M2N(N−1)+k=1∑MG∈S∑H∈C(Gk)∑1
到这里应该都挺好理解的。
下面,我们将推导如何为每个
k 求出
∑G∈S∑H∈C(Gk)1。
对于一个
H,我们设它有
V 个点与
L 条边,那么有
H∈C(Gk) 的图
G∈S 的
H 的数量有如下数量个。
(M−k)2V(V−1)−L×kL×(M−k)V(N−V)×M2(N−V)(N−V−1)
对于两个端点都在
H 中的边,由于只能有
L 条边显示出来,即有
L 条边的权值为
0∼k−1,共
k 种取值,于是一共有
kL 种情况,剩下共
2V(V−1)−L 条边由于都没显现出来,于是取值范围从
k∼M−1,共
M−k 种取值,于是有
(M−k)2V(V−1)−L 种情况。
对于只有一个端点在
H 中的边,由于这种边如果显现,那么
H 就和
H 外的点连接了,与定义不符,于是这种边取值范围从
k∼M−1,共
M−k 种取值,于是有
(M−k)V(N−V) 种情况。
对于两个端点都不在
H 中的边,由于与
H 无关,所以就可以随便选,有
M2(N−V)(N−V−1) 种取值。
全部乘起来即可。
我们发现后两项只与
V 有关,于是可以设
f(s) 表示有
s 个节点的所有联通块
H 的
(M−k)2s(s−1)−L×kL 的总和。
由于我们要在
N 个点中选
s 个点组成联通块,于是要乘上
(sN)。
根据乘法分配律,原式就可化为:
(N−1−M)×M2N(N−1)+k=1∑Ms=1∑N(sN)f(s)×(M−k)s(N−s)×M2(N−s)(N−s−1)
现在我们的问题是如何快速求出
f(s)。
考虑容斥,于是可以用总方案数减去不构成联通块的方案数。
不构成联通块的方案数就是有小联通块独立出来,这和上面的式子很像。
于是我们直接 copy 过来。
f(s)=M2s(s−1)−i=1∑s−1(is)f(i)×(M−k)i(s−i)×M2(s−i)(s−i−1)
于是我们开心的打了代码,发现样例没过。
为什么?
我们发现,由于你连通块外的边乱选,就有可能也组成连通块,于是
(is) 的时候会算重。
考虑如何去重。
我们可以钦定一个点一定在所选连通块内,那么无论其他如何组,都因为没有钦定的点而选不到。
于是就相当于
(i−1s−1)。
原式即为:
f(s)=M2s(s−1)−i=1∑s−1(i−1s−1)f(i)×(M−k)i(s−i)×M2(s−i)(s−i−1)
时间复杂度
O(n2m)。
code
CPP#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=510;
const int mod=998244353;
int n,m,ans;
int f[N];
int c[N][N],pp[N][N*N];
signed main()
{
cin>>n>>m;
for(int i=0;i<=500;i++){
c[i][0]=1;
for(int j=1;j<=i;j++) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
pp[i][0]=1;
for(int j=1;j<=500*500;j++) pp[i][j]=pp[i][j-1]*i%mod;
}
ans=(n-1-m)*pp[m][n*(n-1)/2]%mod;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
f[j]=pp[m][j*(j-1)/2];
for(int k=1;k<j;k++) f[j]=(f[j]-f[k]*c[j-1][k-1]%mod*pp[m-i][k*(j-k)]%mod*pp[m][(j-k)*(j-k-1)/2]%mod+mod)%mod;
ans=(ans+c[n][j]*f[j]%mod*pp[m-i][j*(n-j)]%mod*pp[m][(n-j)*(n-j-1)/2]%mod)%mod;
}
}
cout<<ans;
return 0;
}