思路
考虑钦定 k 个点深度为奇数,这部分方案数为 (k−1n−1).
树是一个二分图,考虑将其黑白染色.我们可以发现深度为奇数的点颜色相同,于是剩下的部分转化成了计算完全二分图 Kk,n−k 的生成树数目.我们有如下引理:
Kn,m 的生成树数目为 nm−1mn−1.
网上的大部分题解貌似都用的 Prüfer 序列进行证明,这里给出一种利用矩阵树定理的证明方式.
对 Kn,m 应用矩阵树定理,其生成树个数为:
∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣m0⋮0−1−1⋮−10m⋮0−1−1⋮−1⋯⋯⋱⋯⋯⋯⋱⋯00⋮m−1−1⋮−1−1−1⋮−1n0⋮0−1−1⋮−10n⋮0⋯⋯⋱⋯⋯⋯⋱⋯−1−1⋮−100⋮n∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣
其中,主对角线上为 m 的元素一共有 n−1 个(去掉一行一列),为 n 的元素有 m 个.
使用前 n−1 行逐一消去左下区域内的 −1,原式可化为:
∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣m0⋮000⋮00m⋮000⋮0⋯⋯⋱⋯⋯⋯⋱⋯00⋮m00⋮0−1−1⋮−1n−mn−1−mn−1⋮−mn−1−1−1⋮−1−mn−1n−mn−1⋮−mn−1⋯⋯⋱⋯⋯⋯⋱⋯−1−1⋮−1−mn−1−mn−1⋮n−mn−1∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣
将最后 m−1 行加到倒数第 m 行上,可得:
∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣m0⋮000⋮00m⋮000⋮0⋯⋯⋱⋯⋯⋯⋱⋯00⋮m00⋮0−1−1⋮−11−mn−1⋮−mn−1−1−1⋮−11n−mn−1⋮−mn−1⋯⋯⋱⋯⋯⋯⋱⋯−1−1⋮−11−mn−1⋮n−mn−1∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣
再将倒数第 m 行乘 mn−1 后加在最后 m−1 行上,可得:
∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣m0⋮000⋮00m⋮000⋮0⋯⋯⋱⋯⋯⋯⋱⋯00⋮m00⋮0−1−1⋮−110⋮0−1−1⋮−11n⋮0⋯⋯⋱⋯⋯⋯⋱⋯−1−1⋮−110⋮n∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣∣
显然上式等于 nm−1mn−1,得证.
那么这题的答案就是 (k−1n−1)kn−k−1(n−k)k−1.
代码
#include <cstdio>
inline int rd() {
int x = 0, f = 1, c = getchar();
while (((c - '0') | ('9' - c)) < 0)
f = c != '-', c = getchar();
while (((c - '0') | ('9' - c)) > 0)
x = x * 10 + c - '0', c = getchar();
return f ? x : -x;
}
using i64 = long long;
const int N = 5e5;
int n, k, P;
i64 fpow(i64 b, i64 p) {
i64 res = 1;
for(; p; b = b * b % P, p >>= 1) {
if(p & 1) res = res * b % P;
}
return res;
}
i64 fac[N + 5], ifac[N + 5];
void pre() {
fac[0] = 1;
for(int i = 1; i <= N; i++) fac[i] = fac[i - 1] * i % P;
ifac[N] = fpow(fac[N], P - 2);
for(int i = N - 1; i >= 0; i--) ifac[i] = ifac[i + 1] * (i + 1) % P;
}
i64 C(int n, int m) { return fac[n] * ifac[m] % P * ifac[n - m] % P; }
int main() {
n = rd(), k = rd(), P = rd(); pre();
printf("%lld\n", fpow(k, n - k - 1) * fpow(n - k, k - 1) % P * C(n - 1, k - 1) % P);
return 0;
}