题目链接:Codeforces1109D
题意
求满足以下条件的带标号树的个数:
-
边权在([1,m]cap Z)
-
点(a)到点(b)间的边权和为(m)
数据范围:(n,m≤1e6)
题解
枚举(a),(b)间点数为(i),可以知道方案数为:
[dbinom{n - 2}{i}dbinom{m-1}{i}i!m^{n-2-i} f(i)
]
(dbinom{n-2}{i}i!) 是在剩下(n-2)个点中选(i)个排在(a),(b)之间。
(dbinom{m-1}{i}) 是(i+1)条边和为(m)的方案数。
(m^{n-2-i}) 是不在(a),(b)之间的点可以自由选择权值。
(f(i)) 是有一个长为(i+2)的链(以(a),(b)为端点)和(n-i-2)个独立的点的生成树个数。
事实上,要计算(f(i)),我们可以用一个更强的结论:
已知有(n)个点和(k)个联通的点集,第(i)个点集大小为(size[i]),那么它生成树的个数为:
[(Pi_{i=1} ^ {k} size[i])n^{k-2}
]
设(P)是长度为(k-2)的(prufer)序列的集合,(P)中的每个元素和一个(k)个点的生成树一一对应。
但和一般的(k)个点生成树不同。这里第(i)个点的度数(+1),会使答案乘上(size[i])。
设第(i)个点集出现在(prufer)序列中的次数为(c_i),那么我们要求的答案就是:
[sum_{pin P} Pi_{i=1}^{k} size[i]^{c_i+1}
]
把那个(+1)提到外面去,就是考虑计算
[sum_{pin P} Pi_{i=1}^{k} size[i]^{c_i}
]
因为(sum c_i = k-2) , 这就相当于走(k-2)步,如果走第(i)条就产生(size[i])的贡献,求所有方案下的贡献和。
这个就是((sum size[i]) ^ {k-2} = n ^ {k - 2})
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
int n, m, a, b;
int fac[N], fav[N], inv[N];
int Qpow(int x, int p) {
if (p == -1) return inv[x];
int ans = 1;
while (p) {
if (p & 1) ans = 1LL * ans * x % mod;
x = 1LL * x * x % mod;
p >>= 1;
}
return ans;
}
int C(int x, int y) {
if (x < y || y < 0) return 0;
return 1LL * fac[x] * fav[y] % mod * fav[x - y] % mod;
}
void upd(int &x, int y) {
(x += y) >= mod ? x -= mod : 0;
}
int main() {
cin >> n >> m >> a >> b;
int ans = 0;
fac[0] = fav[0] = 1;
for (int i = 1; i < N; ++i) {
inv[i] = Qpow(i, mod - 2);
fac[i] = 1LL * fac[i - 1] * i % mod;
fav[i] = 1LL * fav[i - 1] * inv[i] % mod;
}
for (int i = 0; i <= n - 2; ++i) {
int tmp = 1LL * C(n - 2, i) * C(m - 1, i) % mod;
tmp = 1LL * tmp * fac[i] % mod;
tmp = 1LL * tmp * Qpow(m, n - 2 - i) % mod;
tmp = 1LL * tmp * (i + 2) % mod;
tmp = 1LL * tmp * Qpow(n, n - i - 3) % mod;
upd(ans, tmp);
}
cout << ans << endl;
return 0;
}