Loading

CF1615F LEGOndary Grandmaster (组合数)

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;
}
posted @ 2024-03-23 20:23  Fire_Raku  阅读(8)  评论(0编辑  收藏  举报