CF1615F LEGOndary Grandmaster (组合数)

CF1615F LEGOndary Grandmaster

计数好题,转换条件+转化贡献+组合数

首先题目的操作没有什么好的性质,考虑一个经典的 trick,将奇数位置上的数字取反,于是题目的操作变成 01101001。这个操作的性质就是序列中 1 的总数不变,并且操作可以抽象成「使 1 在序列上移动」

转化完条件,能使 st 相等的条件即 1 的个数相等。假设 1 的个数相等,此时考虑如何计算贡献。

s 序列中第 i1 的位置为 ait 序列中第 i1 的位置为 bi,那么答案就是 |aibi|

但是这么做在有问号的情况下并不好计算,考虑另一个 trick,我们将贡献转化成每一个空隙被跨过的次数。也就是对于每个 1i<n 的前缀 si 和前缀 ti 的差值的绝对值(多出的部分一定需要从 ii+1)。

枚举每个间隙计算答案,考虑枚举左边的差值,不妨设 A 表示 s[1i] 中的问号数量,B 表示 t[1i] 的问号数量,此时一部分问号会变成 1,若 s 中问号变成 1 的数量为 j,两者差值为 d,则有方案数

jCAjCBj+d

jCAjCBBjd

这个形式可以用范德蒙德卷积,化成一个组合数

CA+BBd

[i+1,n] 的差值由 [1,i] 的差值可以推出,方案数计算方法类似,最后两边方案数相乘再乘跨过次数就是差值为 j 时的贡献。

于是得到了 O(n2) 的做法。

#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 @   Fire_Raku  阅读(10)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示