1 条题解
-
0
「JOI 2013 Final」彩灯 题解
问题分析
我们有一个长度为 的 序列,表示彩灯的亮灭状态。可以最多翻转一个连续区间( 变 , 变 ),目标是最大化最长的交替序列的长度。
交替序列定义为相邻元素不同的连续子序列。
关键观察
1. 交替序列的性质
对于一个序列,如果它是交替的,那么相邻元素必须不同。用差分的方式思考:
- 定义 (异或运算)
- 在交替序列中, 应该全为 (除了边界)
2. 翻转操作的影响
翻转一个区间 会:
- 改变 和 的值(如果存在)
- 区间内部的 值不变
这是因为翻转操作只影响区间的两个边界处的相邻关系。
算法思路
1. 预处理差分数组
for (int i = 1; i <= n; i++) b[i] = (a[i] ^ a[i - 1]);这里 表示 和 是否相同:
- 表示
- 表示
2. 找到所有"断裂点"
vector<int> w; for (int i = 2; i <= n; i++) if (!b[i]) w.push_back(i);存储所有不满足交替条件的位置,即 的位置。
3. 特殊情况处理
if (sz <= 1) { printf("%d\n", n); return 0; }如果断裂点少于 个,说明原序列几乎就是交替的,翻转一个区间后可以使得整个序列变成交替序列,答案为 。
4. 计算每个位置的左右交替长度
for (int l = 2, r; l <= n; l = r + 1) { r = l; if (!b[r]) continue; while (r <= n && b[l] == b[r]) r++; r--; R[l - 1] = r - l + 1; L[r + 1] = r - l + 1; }这里计算:
- :以位置 为右端点的最长交替序列长度
- :以位置 为左端点的最长交替序列长度
5. 寻找最优翻转位置
for (int i = 1; i < sz; i++) ans = max(ans, L[w[i - 1]] + R[w[i - 1]] + R[w[i]] + 3);考虑翻转包含两个相邻断裂点 和 的区间,这样可以将它们连接起来。
算法正确性证明
1. 翻转操作的分析
当我们翻转区间 时:
- 边界 和 的关系改变: 翻转
- 边界 和 的关系改变: 翻转
- 区间内部的相邻关系不变
因此,翻转操作实际上只改变了两个位置的 值。
2. 连接策略
假设我们有两个断裂点 和 (),翻转区间 :
- 翻转:可能修复 处的断裂
- 翻转:可能修复 处的断裂
- 这样可以将 左侧的交替段和 右侧的交替段连接起来
3. 长度计算
对于断裂点 和 :
- : 左侧的交替段长度
- : 右侧的交替段长度(实际上这里代码可能有误,应该是 )
- 连接后的总长度 = 左段 + 中间修复的部分 + 右段
复杂度分析
- 预处理差分数组:
- 寻找断裂点:
- 计算左右交替长度:
- 寻找最优解:,其中 是断裂点数量
总复杂度:
示例验证
样例 1
输入:
10 1 1 0 0 1 0 1 1 1 0处理过程:
- 原序列:
1 1 0 0 1 0 1 1 1 0 - 差分 :
? 0 1 0 1 1 1 0 0 1(第一个位置无意义) - 断裂点:位置 处
- 计算左右交替段长度
- 找到最优连接方案,得到长度
样例 3
输入:
5 1 1 0 1 1处理过程:
- 原序列:
1 1 0 1 1 - 差分 :
? 0 1 1 0 - 断裂点:位置
- 翻转区间 得到:
1 0 1 0 1 - 整个序列变成交替序列,长度
总结
本题的关键在于:
- 差分转换:将交替序列问题转化为寻找连续 的问题
- 翻转分析:理解翻转操作只影响两个边界
- 连接策略:通过翻转连接相邻的交替段
这种方法在 时间内解决了问题,适用于 的大规模数据。
- 1
信息
- ID
- 5353
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者