CF1615F LEGOndary Grandmaster (组合数)
计数好题,转换条件+转化贡献+组合数
首先题目的操作没有什么好的性质,考虑一个经典的 trick,将奇数位置上的数字取反,于是题目的操作变成 \(01\rightarrow 10\) 或 \(10 \rightarrow 01\)。这个操作的性质就是序列中 \(1\) 的总数不变,并且操作可以抽象成「使 \(1\) 在序列上移动」。
转化完条件,能使 \(s\) 和 \(t\) 相等的条件即 \(1\) 的个数相等。假设 \(1\) 的个数相等,此时考虑如何计算贡献。
设 \(s\) 序列中第 \(i\) 个 \(1\) 的位置为 \(a_i\),\(t\) 序列中第 \(i\) 个 \(1\) 的位置为 \(b_i\),那么答案就是 \(\sum|a_i-b_i|\)。
但是这么做在有问号的情况下并不好计算,考虑另一个 trick,我们将贡献转化成每一个空隙被跨过的次数。也就是对于每个 \(1\le i< n\) 的前缀 \(s_i\) 和前缀 \(t_i\) 的差值的绝对值(多出的部分一定需要从 \(i\rightarrow i+1\))。
枚举每个间隙计算答案,考虑枚举左边的差值,不妨设 \(A\) 表示 \(s[1\sim i]\) 中的问号数量,\(B\) 表示 \(t[1\sim i]\) 的问号数量,此时一部分问号会变成 \(1\),若 \(s\) 中问号变成 \(1\) 的数量为 \(j\),两者差值为 \(d\),则有方案数
\[\sum_j C_A^j C_B^{j+d}
\]
\[\sum_j C_A^j C_B^{B-j-d}
\]
这个形式可以用范德蒙德卷积,化成一个组合数:
\[C_{A+B}^{B-d}
\]
\([i+1,n]\) 的差值由 \([1,i]\) 的差值可以推出,方案数计算方法类似,最后两边方案数相乘再乘跨过次数就是差值为 \(j\) 时的贡献。
于是得到了 \(O(n^2)\) 的做法。
#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back
typedef long long i64;
const int N = 2010, mod = 1000000007;
i64 s[N], t[N], ps[3], pt[3], ss[3], st[3];
i64 fac[N << 1], inv[N << 1];
i64 qpow(i64 a, i64 b) {
i64 ret = 1;
while(b) {
if(b & 1) ret = ret * a % mod;
a = a * a % mod;
b >>= 1;
}
return ret;
}
void init() {
fac[0] = 1;
for(int i = 1; i <= (N << 1) - 10; i++) fac[i] = fac[i - 1] * i % mod;
inv[(N << 1) - 10] = qpow(fac[(N << 1) - 10], mod - 2);
for(int i = (N << 1) - 11; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
}
i64 C(i64 n, i64 m) {
if(m < 0 || m > n) return 0;
return fac[n] * inv[m] % mod * inv[n - m] % mod;
}
void Solve() {
int n;
std::cin >> n;
std::string a, b;
std::cin >> a >> b;
a = ' ' + a, b = ' ' + b;
for(int i = 1; i <= n; i++) {
if(a[i] == '?') s[i] = 2;
else s[i] = a[i] - '0';
if(b[i] == '?') t[i] = 2;
else t[i] = b[i] - '0';
if(!(i & 1)) continue;
if(s[i] == 0 || s[i] == 1) s[i] ^= 1;
if(t[i] == 0 || t[i] == 1) t[i] ^= 1;
}
// for(int i = 1; i <= n; i++) {
// std::cout << s[i] << " \n"[i == n];
// }
// for(int i = 1; i <= n; i++) {
// std::cout << t[i] << " \n"[i == n];
// }
for(int i = 0; i <= 2; i++) ps[i] = pt[i] = ss[i] = st[i] = 0;
for(int i = 1; i <= n; i++) ss[s[i]]++, st[t[i]]++;
i64 ans = 0;
for(int i = 1; i < n; i++) {
ss[s[i]]--, st[t[i]]--;
ps[s[i]]++, pt[t[i]]++;
for(i64 j = -n; j <= n; j++) {
i64 d = j - (ps[1] - pt[1]), d2 = j - (st[1] - ss[1]);
ans = (ans + C(ps[2] + pt[2], pt[2] + d) * C(ss[2] + st[2], st[2] - d2) % mod * abs(j) % mod) % mod;
}
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
init();
int T;
std::cin >> T;
while(T--) Solve();
return 0;
}