题解:洛谷 AT_abc426_c Upgrade Required
【题目来源】
洛谷:AT_abc426_c [ABC426C] Upgrade Required - 洛谷
【题目描述】
有 \(N\) 个操作系统版本,按时间顺序编号为 \(1,2,\dots,N\)。有 \(N\) 台电脑,最初第 \(i\) 台电脑的操作系统版本为 \(i\)。
依次执行 \(Q\) 次操作。第 \(i\) 次操作如下:
- 将所有当前操作系统版本为 \(X_i\) 或更早的电脑升级到版本 \(Y_i\)(且 \(Y_i>X_i\))。然后,输出本次操作被升级的电脑数量。
注意,对于 \(i<Q\),第 \(i\) 次操作执行完成后,才会继续进行第 \(i+1\) 次操作。
【输入】
输入按下述格式从标准输入给出:
\(N\) \(Q\)
\(X_1\) \(Y_1\)
\(X_2\) \(Y_2\)
⋮
\(X_Q\) \(Y_Q\)
【输出】
输出 \(Q\) 行。
第 \(i\) 行输出第 \(i\) 次操作中被升级的电脑数量。
【输入样例】
8 5
2 6
3 5
1 7
5 7
7 8
【输出样例】
2
1
0
3
7
【核心思想】
-
问题分析:给定 \(N\) 台电脑,初始版本为 \(a_i = i\)。每次操作将所有版本 \(\le X_i\) 的电脑升级到 \(Y_i\)(\(Y_i > X_i\)),并输出本次升级数量。这是一个区间合并/指针优化模拟问题,关键在于发现"版本 \(\le X\) 的电脑"会随着操作逐渐集中到高版本,利用
minn指针跳过已清空的低版本位置,避免重复遍历。 -
算法选择:
- 指针优化:维护
minn表示当前最小的非零版本位置,若minn > X则无电脑需要升级,直接输出 \(0\) - 值聚合:将版本 \(\le X\) 的所有电脑数量累加到版本 \(Y\),并清空这些低版本位置,实现"低版本向高版本合并"
- 指针优化:维护
-
关键步骤:
- 初始化:\(a[i] = 1\)(\(i\) 从 \(1\) 到 \(N\)),
minn = 1 - 处理每次操作(读入 \(X, Y\)):
- 若
minn > X:输出 \(0\),跳过 - 否则:
- 遍历 \(i\) 从
minn到 \(X\):- 若 \(a[i] \neq 0\):\(cnt \mathrel{+}= a[i]\),\(a[Y] \mathrel{+}= a[i]\),\(a[i] = 0\),
minn = i
- 若 \(a[i] \neq 0\):\(cnt \mathrel{+}= a[i]\),\(a[Y] \mathrel{+}= a[i]\),\(a[i] = 0\),
- 输出 \(cnt\)
- 遍历 \(i\) 从
- 若
- 指针更新:
minn始终指向最近一次被清空的位置,下次操作从此开始
- 初始化:\(a[i] = 1\)(\(i\) 从 \(1\) 到 \(N\)),
-
时间/空间复杂度:
- 时间复杂度:均摊 \(O(N + Q)\),每个位置最多被清空一次,
minn单调递增 - 空间复杂度:\(O(N)\),存储每个版本的电脑数量
- 时间复杂度:均摊 \(O(N + Q)\),每个位置最多被清空一次,
-
指针优化模拟的核心思想:
- 惰性删除 + 指针跳跃:已清空的位置不再重复访问,
minn指针确保每次遍历只处理"活着"的元素 - 值聚合而非逐个移动:将多个低版本的电脑数量直接累加到目标版本 \(Y\),避免逐个模拟每台电脑的升级
- 单调性保证复杂度:由于 \(a[i]\) 一旦被清空不再恢复,
minn只增不减,总遍历次数不超过 \(N\) - 适用于"区间批量操作 + 位置逐渐失效"类问题,核心是用指针跳过无效状态
- 惰性删除 + 指针跳跃:已清空的位置不再重复访问,
【算法标签】
普及- #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005; // 定义最大元素数量
int n; // 元素总数
int q; // 查询次数
int minn; // 当前最小可操作位置
int a[N]; // 存储每个元素的值
int main()
{
minn = 1; // 初始化最小可操作位置为1
// 输入元素总数和查询次数
cin >> n >> q;
// 初始化所有元素的值为1
for (int i = 1; i <= n; i++)
{
a[i] = 1;
}
// 处理每个查询
while (q--)
{
int x, y;
cin >> x >> y; // 输入操作参数
// 如果当前最小位置大于x,直接输出0
if (minn > x)
{
cout << 0 << endl;
}
else
{
int cnt = 0; // 计数器
// 从minn到x遍历
for (int i = minn; i <= x; i++)
{
if (a[i] != 0) // 如果当前元素不为0
{
cnt += a[i]; // 累加当前元素值
a[y] += a[i]; // 将当前元素值加到y位置
a[i] = 0; // 清空当前元素
minn = i; // 更新最小可操作位置
}
}
// 输出本次操作累加的值
cout << cnt << endl;
}
}
return 0;
}
【运行结果】
8 5
2 6
2
3 5
1
1 7
0
5 7
3
7 8
7
浙公网安备 33010602011771号