http://codeforces.com/contest/1109

D. Sasha and Interesting Fact from Graph Theory

 

题意:一棵树n个点,每条边权为1到m,问有多少棵树满足给定两点的距离为m。

组合数学+扩展 Cayley 定理

给定两点其实没什么用,所以不需要管。

两点的路径是一条链,所以枚举链的长度,链上所有边和为m,且每条边为1到m,所以隔板法求出有多少种分配方法。

确定了链之后,还剩下一些点,要把这些点插到链上,问有多少种插法。

就要用到扩展 Cayley 定理了。

Cayley 定理及证明见 https://www.cnblogs.com/lfri/p/10433011.html

扩展 Cayley 定理及证明见 https://kaloronahuang.com/oi/cayley-定理-扩展-cayley-定理/

Cayley 定理 :nn 个有标号的点能组成 nn2n^{n-2} 棵不同的树。

扩展 Cayley 定理 :nn 个有标号的点组成包含 ss 棵树的森林,有 snns1sn^{n-s-1} 种。

对于本题,链的长度为 kk,则链上有 k+1k+1 个点,剩下的点插入后 nn 个点形成 k+1k+1 棵树,有 (k+1)nnk2(k+1)n^{n-k-2} 种。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int INF = 0x3f3f3f3f;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int N = 2e6 + 10;
const ll mod = 1e9 + 7;
const ll inv2 = (mod + 1) / 2;
ll fac[N], inv[N];
ll power(ll a, ll x) {
ll ans = 1;
while (x) {
if (x & 1) ans = (ans * a) % mod;
a = (a * a) % mod;
x >>= 1;
}
return ans;
}
void init() {
fac[0] = 1;
for (int i = 1; i < N; i++) {
fac[i] = fac[i - 1] * i %mod;
}
inv[N - 1] = power(fac[N - 1], mod - 2);
for (int i = N - 2; i >= 0; i--) {
inv[i] = inv[i + 1] * (i + 1) % mod;
}
}
ll n, m, a, b, ans;
ll C(ll n, ll k) {
return fac[n] * inv[n - k] % mod*inv[k] % mod;
}
ll P(ll n, ll k) {
return fac[n] * inv[n - k] % mod;
}
int main() {
scanf("%lld%lld%lld%lld", &n, &m, &a, &b);
init();
for (ll k = 1; k <= min(m, n - 1); k++) {
ll tmp = C(m - 1, k - 1)*P(n - 2, k - 1) % mod;
if (k < n - 1) {
tmp = tmp * (k + 1) % mod*power(n, n - k - 2) % mod*power(m, n - k - 1) % mod;
}
ans = (ans + tmp) % mod;
}
printf("%lld\n", ans);
return 0;
}