Loading

什么是带环 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;
*/
posted @ 2023-10-28 19:23  purplevine  阅读(5)  评论(0编辑  收藏  举报  来源