arc228A Row and Col swap

给定两个长为 \(n\) 的排列 \(P,Q\)。重复 \(m\) 次:执行以下操作之一

  • 选择一对 \(P\) 中的数并交换。
  • 选择一对 \(Q\) 中的数并交换。
  • 选择一个下标 \(i\),并交换 \(P_i,Q_i\)

试计数在所有的 \(n^{2m}\) 种操作中有多少种使得结束后 \(P\),\(Q\) 仍为排列。

\[n,m\le 500 \]


考虑如何刻画 \(P\) 仍为排列的条件。考虑在开始前,对每对 \(P_x=Q_y=i\),将最终位于 \(P\) 中的染为红色,其余的保持黑色。那么最终 \(P\) 仍为排列即相当于其中全为红色。

考虑染色后的操作,对于同一个位置上 \((P_i,Q_i)\) 的颜色,只有 \(4\) 种可能的情况,不妨计每种情况的出现次数为 \(c_{00},c_{01},c_{10},c_{11}\),由于我们有 \(c_{00}+c_{01}+c_{10}+c_{11}=n\)\(c_{00}=c_{11}\),因此只需其中之二即可推出剩下的。不妨记 \(x=c_{00},y=c_{01}\)

若给定初始状态对应的 \((x,y)\),不难在状态间 \(\mathcal O(1)\) 转移,这个过程是 \(\mathcal O(mn^2)\) 的。

考虑计数 \(2^n\) 中染色方案下每种 \((x,y)\) 状态的出现次数。

不妨找出所有的置换环,环间的贡献独立。环的贡献仅与长度有关。

因此对每种长度,DP 记录当前 \((x,y)\),上一个数 \(0/1\),以及第一个数 \(0/1\),即可计数整个环的贡献。复杂度 \(\mathcal O(n^3)\)


#include <algorithm>
#include <iostream>
#include <utility>
#include <cstring>

const int N = 507, O = 998244353;
#define rep(i,a,b) for(int i(a);i<=(b);++i)
auto incr = [](auto& x, auto&& y) { x += y, x -= (x >= O) * O; };
int n, m, n2; int p[N], q[N], r[N];

int vis[N];

int init[N][N];

inline void solve() {
  std::cin >> n >> m, n2 = n / 2;
  rep(i, 1, n) std::cin >> p[i];
  rep(i, 1, n) std::cin >> q[i], r[p[i]] = q[i];

  init[0][0] = 1; int ta = 0, tb = 0;
  auto contribute = [&](int len) {
    static int tmp[N][N][2][2], res[N][N];
    rep(i, 0, len) rep(j, 0, len) memset(tmp[i][j], 0, 4*sizeof(int));
    tmp[0][0][0][1] = tmp[0][0][1][0] = 1;
    tmp[0][0][0][0] = tmp[0][0][1][1] = 0;
    for(int i = 2; i <= len; ++i) {
      for(int j = (i-1)/2; j >= 0; --j) {
        for(int k = i-2; k >= 0; --k) {
          for(int x: {0, 1}) for(int z: {0, 1}) {
            int ret = std::exchange(tmp[j][k][x][z], 0);
            incr(tmp[j+!x][k][1][z], ret), incr(tmp[j][k+!x][0][z], ret);
          }
        }
      }
    }
    for(int i = 0; i <= len/2; ++i) {
      for(int j = 0; j <= len; ++j) {
        int ret = tmp[i][j][1][0];
        incr(ret, tmp[i][j][1][1]);
        if(i > 0) incr(ret, tmp[i-1][j][0][0]);
        if(j > 0) incr(ret, tmp[i][j-1][0][1]);
        res[i][j] = ret;
      }
    }
    for(int i = ta; i >= 0; --i) {
      for(int j = tb; j >= 0; --j) {
        int ret = init[i][j];
        for(int x = 0; x <= len/2; ++x) {
          for(int y = 0; y <= len; ++y) {
            incr(init[i+x][j+y], 1ull * ret * res[x][y] %O);
          }
        }
        incr(init[i][j], O-ret);
      }
    }
    ta += std::min(n2, (len + 1) / 2), tb += len;
  };

  rep(i, 1, n) {
    if(vis[i]) continue;
    int len = 1, pt = r[i];
    while(pt != i) vis[pt] = 1, pt = r[pt], ++len;
    contribute(len);
  }

  static long long dp[2][N][N];
  rep(i, 0, n2) rep(j, 0, n) dp[0][i][j] = init[i][j];

  auto c2 = [](int x) { return x * (x - 1); };
  rep(t, 1, m) {
    auto &&f = dp[(t-1)&1], &&g = dp[t&1];
    rep(i, 0, n2) rep(j, 0, n) g[i][j] = 0;
    rep(a, 0, n2) rep(b, 0, n) {
      int d = a, c = n - a - b - d;
      // swap col
      incr(g[a][b], f[a][b] * (a + d) %O);
      if(b > 0) incr(g[a][b-1], f[a][b] * b %O);
      if(c > 0) incr(g[a][b+1], f[a][b] * c %O);
      // swap row
      incr(g[a][b], f[a][b] * (c2(a) + c2(b) + c2(c) + c2(d) + 2 * (a+d) * (b+c)) %O);
      if(a > 0) incr(g[a-1][b+1], f[a][b] * 2 * a * d %O);
      if(b > 0) incr(g[a+1][b-1], f[a][b] * 2 * b * c %O);
    }
  }

  std::cout << dp[m&1][0][0] << "\n";
}

int main() {
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  solve();
}
posted @ 2026-09-02 17:12  CuteNess  阅读(8)  评论(0)    收藏  举报