Loading

Luogu P11361 [NOIP2024] 编辑字符串 题解

Link

观察题。一个有趣的事实是题解中提到的所有错解我还真都想过一次,比如贪心 pick 最长的连续 \(1\) 段以及尝试合并上下连续的 \(1\) 段。这题的数据范围和特殊性质做的不错,顺着来想比较好考虑问题。

对于 \(\rm A\) 情况,\(s_1\) 所有字符都相同,那么答案是一定的;对于 \(t_1 = t_2\) 的情况,由于限制是一定的,这启发我们在一定位置做了移动的限制下,由于不限制移动次数,我们理论上可以将所有连续的不被限制的数任意排出想要的顺序,所以对于 \(\rm B\) 去看自由移动段的 \(0/1\) 匹配个数之和就是答案;对于 \(|t_{1, i} = 0| = |t_{2, i} = 0| = 1\) 的情况是用来让我们毙掉一些假贪心的,比如 edit_2 里的第二组样例,这个性质 \(\rm C\) 提示我们按照 \(0\) 的位置来 split 开序列形如 \([1, p_0) [p_0, p_0], (p_0, n]\)。然后根据 \(\rm B\) 中我们观察到的结论,“连续段中可以任意排列出想要的 \(01\) 串”,我们对于每个位置贪心地去交换做匹配。

具体地,对于正解:由于连续的 \(1\) 可以任意排列,将连续的 \(1\) 串并为一个区间。考虑怎么贪心地匹配获得最大收益,记 \(c1_{i, 0}, c1_{i, 1}, c2_{i, 0}, c2_{i, 1}\) 分别表示对于串 \(s_1, s_2\) 中连续区间的 \(0/1\)\(cnt\),枚举配对串 \(1 \to n\),分类讨论:

  1. 如果 \(t_{1, i} = t_{2, i} = 0\),那么直接检查 \(s_{1, i}\) 是否等于 \(s_{2, i}\) 即可
  2. 如果 \(t_{1, i} = 0, t_{2, i} \neq 0\),检查是否存在 \(c2_{loc(i), s_{1, i}} \gt 0\) 然后更新答案和 \(c2\)
  3. 如果 \(t_{2, i} = 0, t_{1, i} \neq 0\),同上类似地去检查 \(c1\) 并更新
  4. 如果 \(t_{1, i} = t_{2, i} = 1\),也就是都没有限制,我们直接用 之前已经配对固定位置之后 剩下来的 \(0/1\) 个数各自 \(- 1\) 更新。
#include <bits/stdc++.h>

using i64 = long long;

void solve() {
	int n;
	std::cin >> n;
	std::string s1, s2, t1, t2;
	std::cin >> s1 >> s2 >> t1 >> t2;
	s1 = "$" + s1, s2 = "$" + s2;
	t1 = "$" + t1, t2 = "$" + t2;
	std::vector<std::vector<int>> cnt1(n + 2, std::vector<int>(2)),
								  cnt2(n + 2, std::vector<int>(2));
	std::vector<int> to1(n + 1), to2(n + 1);
	int o = 0;
	for (int i = 1; i <= n; i++) {
		if (t1[i] != t1[i - 1])
			to1[i] = ++o;
		else
			to1[i] = to1[i - 1];
		cnt1[to1[i]][s1[i] - '0']++;
	}
	o = 0;
	for (int i = 1; i <= n; i++) {
		if (t2[i] != t2[i - 1])
			to2[i] = ++o;
		else
			to2[i] = to2[i - 1];
		cnt2[to2[i]][s2[i] - '0']++;
	}
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		if (t1[i] == '0' && t2[i] == '0') {
			if (s1[i] == s2[i])
				ans++;
		} else if (t1[i] == '0') {
			if (cnt2[to2[i]][s1[i] - '0']) {
				cnt2[to2[i]][s1[i] - '0']--;
				ans++;
			}
		} else if (t2[i] == '0') {
			if (cnt1[to1[i]][s2[i] - '0']) {
				cnt1[to1[i]][s2[i] - '0']--;
				ans++;
			}
		}
	}
	for (int i = 1; i <= n; i++) {
		if (t1[i] == '1' && t2[i] == '1') {
			if (cnt1[to1[i]][0] && cnt2[to2[i]][0]) {
				cnt1[to1[i]][0]--;
				cnt2[to2[i]][0]--;
				ans++; continue;
			}
			if (cnt1[to1[i]][1] && cnt2[to2[i]][1]) {
				cnt1[to1[i]][1]--;
				cnt2[to2[i]][1]--;
				ans++;
			}
		}
	}
	std::cout << ans << "\n";
}

int main() {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);

	int t;
	std::cin >> t;
	while (t--) {
		solve();
	}
	return 0;
}
posted @ 2025-11-04 14:22  夢回路  阅读(8)  评论(0)    收藏  举报