1 条题解
-
0
题目理解
题目 1955E - Long Inversions 要求:
给定长度为 的二进制字符串 ,选择一个整数 (),可以进行任意次操作:选择连续 个字符,将它们全部翻转(0→1,1→0)。
求最大的 ,使得可以通过若干次操作将字符串变成全 。
解题思路
1. 关键观察
- 同一个子串不需要翻转两次,因为两次翻转等于没有操作。
- 对于固定的 ,我们可以用贪心策略检查是否可行:
- 从左到右处理字符串,保证前 个字符已经是 。
- 如果第 个字符是 ,则必须翻转从 到 的区间(长度为 )。
- 不能翻转前面的字符,否则会破坏已经变成 的部分。
2. 朴素检查的缺陷
如果每次遇到 都真的去翻转 个字符,检查一个 的时间复杂度是 ,总复杂度会达到 ,无法通过。
3. 优化方法:差分数组 + 翻转计数器
我们可以用一个变量
cnt记录当前字符被翻转的次数(实际只需要奇偶性)。- 当遍历到位置 时,
cnt减去在 处结束的翻转次数(用end[i]记录)。 - 当前字符的实际值 = 原值 (
cnt % 2)。 - 如果实际值是 ,则需要从 开始进行一次翻转:
- 在 处标记这次翻转结束:
end[i+k]++ cnt++(增加一次翻转影响)- 当前字符变为
- 在 处标记这次翻转结束:
这样,每次遇到 只需要 时间更新,整个检查过程是 。
4. 算法步骤
- 从大到小枚举 ( 到 )。
- 对每个 用 时间检查可行性:
- 初始化
cnt = 0,end数组全 。 - 遍历 到 :
cnt -= end[i]- 实际值 =
- 如果实际值为 :
- 若 ,则进行翻转:
end[i+k]++,cnt++ - 否则,这个 不可行
- 若 ,则进行翻转:
- 遍历结束后,检查是否所有位置都变成
- 初始化
- 找到第一个可行的 并输出。
5. 复杂度分析
- 枚举 : 次
- 每次检查:
- 总复杂度:
- 题目保证所有测试用例的 ,可以接受。
代码详解
void solve() { int n; string s; cin >> n >> s; // 从大到小枚举 k for (int k = n; k > 0; --k) { vector<char> t(n), end(n + 1); // 将字符转为数字 0/1 for (int i = 0; i < n; ++i) { t[i] = s[i] - '0'; } int cnt = 0; // 当前翻转次数(只关心奇偶) for (int i = 0; i < n; ++i) { cnt -= end[i]; // 移除在 i 处结束的翻转 t[i] ^= (cnt & 1); // 应用翻转效果 if (t[i] == 0) { // 需要翻转 if (i + k <= n) { ++end[i + k]; // 标记翻转结束位置 ++cnt; // 增加翻转次数 t[i] = 1; // 当前位置变成 1 } else { break; // 无法完成 } } } // 检查是否全为 1 if (*min_element(all(t)) == 1) { cout << k << '\n'; return; } } assert(false); // 理论上至少 k=1 可行 }
示例验证
输入:
5 00100枚举 :检查失败(最后一个 无法覆盖)
:检查失败
:检查成功,输出符合题意。
- 1
信息
- ID
- 6193
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者