CF 2134F Permutation Oddness

这场 div2 真的 D 最难吧???

因为 \(\operatorname{lowbit}\) 的定义就是从低位入手的,那么也就可以尝试把 \(b_{1\sim n}\) 拆位看做 \(c_{0\sim 1, 1\sim n}\),其中 \(c_{w, i}\)\(b_i\) 二进制表示下的第 \(w\) 位。

因为 \(\operatorname{lowbit}(x) = 2\) 首先需要保证 \(x\) 二进制表示下第 \(0\) 位需要为 \(0\),也就是 \(\operatorname{lowbit}\) 取到高位需要考虑低位信息,所以此时得到了一个天然的考虑顺序:

  • 先处理 \(\operatorname{lowbit}(b_i\oplus b_{i + 1}) = 1\) 的位置,再处理剩余的 \(\operatorname{lowbit}(b_i\oplus b_{i + 1}) = 2\) 的位置。

对于 \(\operatorname{lowbit}(b_i\oplus b_{i + 1}) = 1\) 的位置,只要求 \(c_{0, i}\not = c_{0, i + 1}\),算完对应贡献后,就只需要考虑 \(c_1\) 了。

分析对后续计算的影响,发现 \(c_{0, i}\not = c_{0, i + 1}\) 相当于在 \((i, i + 1)\) 隔离两边得到了许多个区间,且每个区间贡献相互独立

此时每个区间内部的 \(c_{0, i}\) 都相同,这说明可以对于 \(c_0\) 的贡献,可以把 \(02, 13\) 放在一起看,只有到 \(c_1\) 时才需要去区分高位的值。

于是此时有个想法是,如果记录下 \(02, 13\) 分别分出了多少个段,且内部贡献是多少,就可以在 \(\mathcal{O}(n^3)\) 的复杂度内合并贡献。

具体来说,可以枚举 \(02\) 分出了 \(i\) 个段,\(13\) 分出了 \(j\) 个段,发现只有 \(|i - j|\le 1\) 时才可能有分段的方式,所以实际有用的 \((i, j)\) 对是 \(\mathcal{O}(n)\) 的。
那么就可以知道 \(\operatorname{lowbit} = 1\) 的对数就是 \(i + j - 1\),于是只需要枚举 \(02, 13\) 的段之间各有多少个 \(\operatorname{lowbit} = 2\) 的对就可以计算答案了。

因为 \(02\)\(13\) 的计算都只关心高位,所以是本质相同的,此处以 \(02\) 举例。

我们要求解的就是,对于 \(02\) 的任意排列,把 \(02\) 分做 \(x\) 段,且内部 \(\operatorname{lowbit} = 2\) 的数量为 \(y\) 的方案数。

首先考虑就算只有一段该怎么做,发现依然要考虑在 \(c_1\) 上的 \(01\) 交替,即此时仍然需要关心 \(0, 2\) 各自的段数。
于是依然枚举 \(0, 2\) 各自的段数 \(i, j\),这时枚举量是 \(\mathcal{O}(n)\) 的。

此时就可以推出在没有被隔离,也就是只有一段时有 \(cnt_1 = i + j - 1\)\(\operatorname{lowbit} = 2\)\(cnt_2 = c_0 + c_2 - 1 - cnt_1\)\(\operatorname{lowbit} = 0\)

此时再考虑被隔离出的段数,其实就是隔离的位置 \(+1\),于是可以直接枚举 \(0\le d_1\le cnt_1, 0\le d_0\le cnt_0\) 表示 \(\operatorname{lowbit} = 2 / 0\) 对应被隔离的位置的数量,就可以推出段数和没被隔离的 \(\operatorname{lowbit} = 2\) 的数量。

这一部分的复杂度依然为 \(\mathcal{O}(n^3)\)

总时间复杂度就为 \(\mathcal{O}(n^3)\)

中间计数时会带一些组合数,这部分是比较容易的,可以参考代码。

还需要注意的是,如果段数 \(i \not = j\),那么开头的类型是确定的,如果 \(i = j\),那么开头的类型 \(2\) 种皆可,需要乘上对应的系数。

#include <bits/stdc++.h>

using ll = long long;

constexpr ll mod = 1e9 + 7;
constexpr int N = 810;

inline void Add(ll &x, const ll y) {
  (x += y) %= mod;
}

ll C[N][N];
inline void init() {
  for (int i = 0; i < N; i++) {
    for (int j = C[i][0] = 1; j <= i; j++) {
      C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
    }
  }
}

ll low0[N][N], low1[N][N];
ll ans[N * 2];

inline void solve() {
  int c0, c1, c2, c3;
  scanf("%d%d%d%d", &c0, &c1, &c2, &c3);
  const int cn = c0 + c1 + c2 + c3;

  for (int i = 1; i <= cn; i++) {
    for (int j = 0; j <= cn; j++) {
      low0[i][j] = low1[i][j] = 0;
    }
  }
  for (int i = 1; i <= c0; i++) {
    for (int j = 1; j <= c2; j++) {
      if (abs(i - j) > 1) {
        continue;
      }
      const ll cnt_02 = C[c0 - 1][i - 1] * C[c2 - 1][j - 1] % mod * (i == j ? 2ll : 1ll) % mod;
      const int cnt1 = i + j - 1, cnt0 = c0 + c2 - 1 - cnt1;
      for (int del1 = 0; del1 <= cnt1; del1++) {
        for (int del0 = 0; del0 <= cnt0; del0++) {
          Add(low0[del1 + del0 + 1][cnt1 - del1], cnt_02 * C[cnt1][del1] % mod * C[cnt0][del0] % mod);
        }
      }
    }
  }
  for (int i = 1; i <= c1; i++) {
    for (int j = 1; j <= c3; j++) {
      if (abs(i - j) > 1) {
        continue;
      }
      const ll cnt_13 = C[c1 - 1][i - 1] * C[c3 - 1][j - 1] % mod * (i == j ? 2ll : 1ll) % mod;
      const int cnt1 = i + j - 1, cnt0 = c1 + c3 - 1 - cnt1;
      for (int del1 = 0; del1 <= cnt1; del1++) {
        for (int del0 = 0; del0 <= cnt0; del0++) {
          Add(low1[del1 + del0 + 1][cnt1 - del1], cnt_13 * C[cnt1][del1] % mod * C[cnt0][del0] % mod);
        }
      }
    }
  }

  for (int i = 0; i <= (cn - 1) * 2; i++) {
    ans[i] = 0;
  }
  for (int i = 1; i <= c0 + c2; i++) {
    for (int j = 1; j <= c1 + c3; j++) {
      if (abs(i - j) > 1) {
        continue;
      }
      const ll ex_cnt = i == j ? 2ll : 1ll;
      for (int cnt1_0 = 0; cnt1_0 < c0 + c2; cnt1_0++) {
        for (int cnt1_1 = 0; cnt1_1 < c1 + c3; cnt1_1++) {
          Add(ans[cnt1_0 * 2 + cnt1_1 * 2 + i + j - 1], low0[i][cnt1_0] * low1[j][cnt1_1] % mod * ex_cnt % mod);
        }
      }
    }
  }

  for (int i = 0; i <= (cn - 1) * 2; i++) {
    printf("%lld%c", ans[i], " \n"[i == (cn - 1) * 2]);
  }
}

int main() {
  init();
  int t;
  scanf("%d", &t);
  while (t--) {
    solve();
  }
  return 0;
}
posted @ 2025-11-18 20:39  rizynvu  阅读(21)  评论(0)    收藏  举报