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 定理 :n 个有标号的点能组成 nn−2 棵不同的树。
扩展 Cayley 定理 :n 个有标号的点组成包含 s 棵树的森林,有 snn−s−1 种。
对于本题,链的长度为 k,则链上有 k+1 个点,剩下的点插入后 n 个点形成 k+1 棵树,有 (k+1)nn−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; }
|