Article Index

ABC 214 G Three Permutations 做题笔记

Published on

题意

给定长度为 $N$ 的两个排列 $p$ 和 $q$,求长度为 $N$ 的排列 $r$ 的个数,使得 $r_i \neq p_i$ 且 $r_i \neq q_i$,对于任意的 $1 \leq i \leq N$ 都成立。

$1 \leq N \leq 3000$

题解

设 $f_i$ 表示任意选 $i$ 个位置,对于这些位置都有 $r_i = p_i$ 或 $r_i = q_i$。根据容斥,答案是

$$\sum\limits_{0 \leq i \leq N}\left(-1\right)^if_i\left(n - i\right)!$$

考虑 $f_i$ 的计算。由于 $r_i$ 不能重复导致不好计数,我们可以换一种思路,让限制匹配位置。具体来说,在 $p_i$ 和 $q_i$ 之间连边,如此在图中,限制 $i$ 被满足表示为把 $p_i$ 或 $q_i$ 分配给了 $\left(p_i, q_i\right)$ 这条边。

还是不好计数,但还有救,把 $\left(p_i, q_i\right)$ 变成 $\left(p_i, s_i\right)$ 和 $\left(s_i, q_i\right)$,相当于在这两条边之间新增一个点。于是点到边的分配被转化成了点到点的匹配。

没什么用,我们需要更多性质。发现图中每个点刚好有一个入和一个出,构成若干个环(可能包含自环)。环之间显然是独立的。于是我们可以对环求出答案然后 DP 合并。

环内的答案是好计算的。假设有 $n$ 个点(包含我们新增的 $s$ 系列点),我们要选出 $k$ 组匹配。如果 $\left(1, n\right)$ 不被选为匹配,那么方案数是 $\binom{n - k}k$。如果 $\left(1, n\right)$ 被选为匹配,那么方案数是 $\binom{n - 2 - \left(k - 1\right)}{k - 1}$。于是相加得到总方案数

$$\binom{n - k}k + \binom{n - k - 1}{k - 1}$$


其实是有 $\tilde{\mathcal O}\left(n\right)$ 做法,不过要多项式,果断放弃。想学可以看这里

代码

/* Please submit with C++17! It's best to use C++20 or higher version.
 * No header file and no RBLIB (https://git.rbtr.ee/root/Template).
 * By Koicy (https://koicy.ly)
 * Email n@rbtr.ee
 * I've reached the end of my fantasy.

         __    __                             __         _
   _____/ /_  / /_________  ___              / /______  (_)______  __
  / ___/ __ \/ __/ ___/ _ \/ _ \   ______   / //_/ __ \/ / ___/ / / /
 / /  / /_/ / /_/ /  /  __/  __/  /_____/  / ,< / /_/ / / /__/ /_/ /
/_/  /_.___/\__/_/   \___/\___/           /_/|_|\____/_/\___/\__, /
                               SIGN                         /___*/
#ifndef XCODE
constexpr bool _CONSOLE = false;
#else
constexpr bool _CONSOLE = true;
#endif
#define __NO_MAIN__ false
#define __ENABLE_RBLIB__ true
constexpr bool _MTS = false, SPC_MTS = false;
constexpr char EFILE[] = "";
#define FULL(arg) arg.begin(), arg.end()

// :/

signed STRUGGLING([[maybe_unused]] unsigned long TEST_NUMBER) {
  constexpr tp mod = 1e9 + 7;
  tp n = bin;
  vetp p = bin.vcr(n, 1);
  vetp q = bin.vcr(n, 1);
  vector<vetp> e(n + 1);
  for (tp i = 1; i <= n; ++i) {
    e[p[i]].push_back(q[i]);
    e[q[i]].push_back(p[i]);
  }
  vector<tp> sec { 0 };
  set<tp> vis;
  for (tp i = 1; i <= n; ++i) {
    auto dfs = [&](auto $, tp x, tp& c) -> void {
      if (vis.count(x)) return;
      ++c;
      vis.insert(x);
      for (auto& j : e[x]) $($, j, c);
    };
    if (vis.count(i)) continue;
    sec.emplace_back();
    dfs(dfs, i, sec.back());
  }
  vector<vetp> f(sec.size(), vetp(n + 1));
  vetp fact(2 * n + 1, 1);
  vetp ifac(2 * n + 1);
  for (tp i = 2; i <= 2 * n; ++i) fact[i] = fact[i - 1] * i % mod;
  for (tp i = 0; i <= 2 * n; ++i) ifac[i] = RCAL::inverse(fact[i], mod);
  auto binom = [&](tp n, tp m) -> tp {
    if (n < 0 || m < 0 || n < m) return 0;
    return fact[n] * ifac[m] % mod * ifac[n - m] % mod;
  };
  f[0][0] = 1;
  for (tp i = 1; i < sec.size(); ++i) {
    if (sec[i] == 1) {
      f[i][0] = f[i - 1][0];
      for (tp j = 1; j <= n; ++j) f[i][j] = (f[i - 1][j] + f[i - 1][j - 1]) % mod;
      continue;
    }
    for (tp j = 0; j <= n; ++j) {
      for (tp k = 0; k <= sec[i] && j + k <= n; ++k) {
        tp l = 2 * sec[i];
        f[i][j + k] = (f[i][j + k] + f[i - 1][j] * (binom(l - k, k) + binom(l - k - 1, k - 1))) % mod;
      }
    }
  }
  tp tar = 0;
  for (tp i = 0; i <= n; ++i) {
    tp t = (i & 1 ? -1 : 1) * fact[n - i];
    tar = (tar + t * f.back()[i]) % mod;
  }
  bin << (tar + mod) % mod << '\n';
  return 0;
}

void MIST() {
}

// :\ */

No comments

Post a comment