题意
分析
向
n
n
n 个点中加入
m
m
m 条边,形成
n
−
m
n-m
n−m 个联通块,那么每个联通块必然为一棵树。利用反证法可以证明:如果一个大小为
x
x
x 的联通块含有大于
x
−
1
x-1
x−1 条边,那么最后形成的联通块的数量显然小于
n
−
m
n-m
n−m 个,因为有一些边并没有使得联通块的数量减少
1
1
1。
又因为,对于一棵无根树而言,计算其大小的贡献,相当于选择其根的方案数。此时,再建立一个虚点
0
0
0 并固定其度为
n
−
m
n-m
n−m ,就将
n
−
m
n-m
n−m 棵树的森林转化为
n
+
1
n+1
n+1 个节点的树。我们要求的就是以
0
0
0 为根的树的个数,可以借助 Prufer
序列来解决。因为
0
0
0 号点的度固定为
n
−
m
n-m
n−m,那么它在序列中出现的次数为
n
−
m
−
1
n-m-1
n−m−1,剩余的
n
−
1
−
(
n
−
m
)
n-1-(n-m)
n−1−(n−m) 个位置为
[
1
,
n
]
[1,n]
[1,n]任意选,所以最终的方案数为:
(
n
−
1
n
−
m
−
1
)
⋅
n
m
=
(
n
−
1
m
)
⋅
n
m
\left(\begin{matrix} n-1\\ n-m-1 \end{matrix}\right)·n^m= \left(\begin{matrix} n-1\\ m \end{matrix}\right)·n^m
(n−1n−m−1)⋅nm=(n−1m)⋅nm
(
n
−
1
m
)
\left(\begin{matrix} n-1\\m\end{matrix}\right)
(n−1m) 可以通过公式:
(
n
−
1
)
⋯
(
n
−
m
)
m
⋅
(
m
−
1
)
⋅
⋯
1
\frac{(n-1)\cdots (n-m)}{m·(m-1)·\cdots 1}
m⋅(m−1)⋅⋯1(n−1)⋯(n−m)和递推逆元求出,
n
m
n^m
nm 使用快速幂直接求解。最终的复杂度:
O
(
m
)
O(m)
O(m)
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N=1e7+5;
int inv[N];
ll power(ll x,ll y){
ll res=1;
x%=mod;
while(y){
if(y&1) res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
ll res=1;
for(int i=0;i<m;i++)
res=res*(n-1-i)%mod;
inv[1]=1;
for(int i=2;i<=m;i++)
inv[i]=(mod-1LL*mod/i*inv[mod%i]%mod)%mod;
for(int i=1;i<=m;i++) res=res*inv[i]%mod;
res=res*power(1LL*n,1LL*m)%mod;
printf("%lld\n",res);
return 0;
}
//C(n-1,m)*n^m