什么是带环 prufer?
给一种题解区没有的做法。
我不会 prufer 的构造也不了解带环的 prufer 诶,能不能把它当成黑箱呢。
缩环,假设确定环上点的集合为 $S$,环的度数为 $\sum_{i \in S} (d_i - 2)$,串成环的方案数是 $\dfrac{(|S|-1)!}{2}$,缩环后的树有 $\dfrac{(n - |S|-1)!}{\prod_{i \not\in S} (d_i - 1)! (\sum_{i \in S} (d_i-2) - 1)!}$ 种,注意到与环直接相连的点视作等价,还需要分进 $|S|$ 个等价类中,方案数还需要乘上 $\dfrac{(\sum_{i \in S} (d_i-2))!}{\prod_{i \in S} (d_i - 2)!}$。这里认为阶乘优先级高于求和求积号。
写起来是
$$ \begin{aligned} & \dfrac{(|S|-1)!}{2} \dfrac{(n - |S| - 1)!}{\prod_{i \not\in S} (d_i - 1)! (\sum_{i \in S} (d_i-2) - 1)!} \dfrac{(\sum_{i \in S} (d_i-2))!}{\prod_{i \in S} (d_i - 2)!} \\ = & \dfrac{(|S|-1)!}{2} \dfrac{(n - |S| - 1)!}{\prod_{i \not\in S} (d_i - 1)!} \dfrac{\sum_{i \in S} (d_i-2)}{\prod_{i \in S} (d_i - 2)!} \end{aligned} $$
$f_{i, j, s}$:考虑前 $i$ 数,$|S|=j$,$\sum_{i \in S} d_i=s$,$\dfrac{1}{\prod_{i \not\in S} (d_i - 1)!\prod_{i \in S} (d_i - 2)!}$ 值之和。转移时枚举 $i$ 是否放入 $S$ 集合内,特殊情况是 $d_i = 1$ 时不能放入 $S$。初始化是 $f_{0, 0, 0} = 1$。
转移 $O(1)$,总复杂度 $O(n^3)$,需要一对滚动数组,还需要判 $|S| = n$。
#include <bits/stdc++.h>
const int mod = 1e9 + 7;
int qpow(int a, int b) {
int ans(1);
for (; b; b >>= 1) {
if (b & 1) ans = 1ll * ans * a % mod;
a = 1ll * a * a % mod;
}
return ans;
}
std::vector<int> fac, ifac, inv;
void pre(int n) {
fac.resize(n + 1), ifac.resize(n + 1), inv.resize(n + 1);
fac[0] = ifac[0] = fac[1] = ifac[1] = inv[1] = 1;
for (int i = 2; i <= n; i++) {
fac[i] = 1ll * fac[i - 1] * i % mod;
inv[i] = 1ll * inv[mod % i] * (mod - mod / i) % mod;
ifac[i] = 1ll * ifac[i - 1] * inv[i] % mod;
}
}
void add(int &x, int y) { if ((x += y) >= mod) x -= mod; }
int main() {
int n; scanf("%d", &n);
std::vector<int> d(n);
bool c = 1;
for (int &u : d) scanf("%d", &u), c &= u == 2;
pre(n);
if (c) return printf("%d\n", 1ll * fac[n - 1] * (mod + 1) / 2 % mod), 0;
std::vector f(2, std::vector (n, std::vector<int> (2 * n))); // |S| < n,不用开全
int o = 0;
f[0][0][0] = 1;
for (int i = 0; i < n; i++, o ^= 1) {
f[o ^ 1] = std::vector<std::vector<int>> (n, std::vector<int> (2 * n));
for (int j = 0; j <= i; j++)
for (int s = 0; s < 2 * n; s++) if (f[o][j][s]) {
if (d[i] > 1)
add(f[o ^ 1][j + 1][s + d[i]], 1ll * f[o][j][s] * ifac[d[i] - 2] % mod);
add(f[o ^ 1][j][s], 1ll * f[o][j][s] * ifac[d[i] - 1] % mod);
}
}
int ans = 0;
for (int i = 3; i < n; i++)
for (int j = 2 * i; j < 2 * n; j++)
add(ans, 1ll * fac[i - 1] * fac[n - i - 1] % mod * inv[2] % mod * (j - 2 * i) % mod * f[o][i][j] % mod);
printf("%d\n", ans);
return 0;
}
/*
stupid mistakes:
- 清空滚动数组
- ifac[i] = 1ll * ifac[i] * inv[i] % mod;
- return printf(), 0;
*/
本文来自博客园,作者:purplevine,转载请注明原文链接:https://www.cnblogs.com/purplevine/p/17914824.html