题解:洛谷 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

【核心思想】

  1. 问题分析:给定 \(N\) 台电脑,初始版本为 \(a_i = i\)。每次操作将所有版本 \(\le X_i\) 的电脑升级到 \(Y_i\)\(Y_i > X_i\)),并输出本次升级数量。这是一个区间合并/指针优化模拟问题,关键在于发现"版本 \(\le X\) 的电脑"会随着操作逐渐集中到高版本,利用 minn 指针跳过已清空的低版本位置,避免重复遍历。

  2. 算法选择

    • 指针优化:维护 minn 表示当前最小的非零版本位置,若 minn > X 则无电脑需要升级,直接输出 \(0\)
    • 值聚合:将版本 \(\le X\) 的所有电脑数量累加到版本 \(Y\),并清空这些低版本位置,实现"低版本向高版本合并"
  3. 关键步骤

    • 初始化\(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
        • 输出 \(cnt\)
    • 指针更新minn 始终指向最近一次被清空的位置,下次操作从此开始
  4. 时间/空间复杂度

    • 时间复杂度:均摊 \(O(N + Q)\),每个位置最多被清空一次,minn 单调递增
    • 空间复杂度:\(O(N)\),存储每个版本的电脑数量
  5. 指针优化模拟的核心思想

    • 惰性删除 + 指针跳跃:已清空的位置不再重复访问,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
posted @ 2026-08-27 11:03  团爸讲算法  阅读(3)  评论(0)    收藏  举报