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;
}

浙公网安备 33010602011771号