题解:学而思编程 生活费

【题目来源】

学而思编程:生活费

【题目描述】

\(n\) 名大学生,编号为 \(1∼n\),初始时每个学生的生活费为 \(p_1,p_2,\dots,p_n\),即编号为 \(i\) 的学生的生活费为 \(p_i\)。生活费可能会有两种变化:

①来源个人,由某个学生的经济活动而产生。在某个时刻,某个学生的生活费会直接变成某一个数值,可能升高也可能降低。

②来源学校,在某个时刻,学校会对生活费低于某个标准线的全部学生,实施政策支持,使得他们的生活费全部正好到达标准线。

经过一段时间之后,上述变化发生了 \(m\) 次。现在请你计算每个学生最后拥有多少生活费。

【输入】

第一行两个整数 \(n,m\)

第二行 \(n\) 个整数 \(p_1,p_2,\dots,p_n\)

接下来 \(m\) 行,每行依次表示一次生活费的变化情况:

以字符 \(I\) 开头的表示一次因为个人引起的变化,后接两个整数 \(id\)\(v\)\(id\) 表示生活费发生变化的学生编号,\(v\) 表示生活费变化的结果;

以字符 \(S\) 开头的表示一次因为学校引起的变化,后接一个整数 \(a\)\(a\) 表示学校的标准线;

注意: 保证按照时间先后顺序给出所有变化情况,即第 \(i\) 次变化一定发生在第 \(i+1\) 变化之前。

【输出】

\(n\) 行,第 \(i\) 行一个整数,表示编号为 \(i\) 的学生最后拥有的生活费。

【输入样例】

4 3
10 20 30 40
S 35
I 1 20
S 25

【输出样例】

25
35
35
40

【核心思想】

  1. 问题分析:给定 \(n\) 个学生的初始生活费 \(p_i\),执行 \(m\) 次操作:单点修改(I id v)将某学生生活费设为 \(v\),整体提升(S a)将所有低于 \(a\) 的学生提升到 \(a\)。求最终每个学生的生活费。这是一个逆向处理 + 整体最大值维护问题,关键在于识别整体操作的后效性,从后往前处理避免重复更新。

  2. 算法选择

    • 逆向扫描:从最后一次操作向前处理,维护当前已见的最大标准线 max_line
    • 懒标记思想:整体操作影响所有学生,逆向处理时只需记录一个全局最大值,单点操作时再用该值修正
  3. 关键步骤

    • 初始化:读取 \(n\)(学生数)、\(m\)(操作数)、初始生活费 \(p[1..n]\)
    • 存储操作:遍历 \(i\)\(1\)\(m\)
      • op == 'I':记录 id[i]v[i](单点操作)
      • op == 'S':设 id[i] = 0,记录 v[i](整体操作的标准线)
    • 逆向处理(从 \(i = m\)\(1\)):
      • id[i] == 0(整体操作):max_line = max(max_line, v[i]):更新当前最大标准线
      • 否则若 ans[id[i]] == 0(该学生尚未被赋值):
        • ans[id[i]] = max(v[i], max_line):单点修改值与后续整体操作的最大标准线取较大值
    • 处理未被操作的学生
      • 遍历 \(i\)\(1\)\(n\)
        • ans[i] == 0ans[i] = max(p[i], max_line):初始值与最大标准线取较大值
    • 输出 ans[1..n]
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n + m)\),存储操作 \(O(m)\),逆向处理 \(O(m)\),最终处理 \(O(n)\)
    • 空间复杂度:\(O(n + m)\),存储操作数组和答案数组
  5. 逆向处理的核心思想

    • 整体操作的后效性S a 影响所有当前生活费低于 \(a\) 的学生,后续的单点修改可能覆盖该影响。正向处理需要每次遍历全部学生,逆向处理只需维护一个全局 max_line
    • 时间顺序反转:从后往前扫描时,对于某个单点操作 I id v,该学生最终值应为 max(v, 后续所有整体操作的最大标准线),因为后续的整体操作一定会作用于该学生
    • 首次赋值原则:逆向处理中每个学生的第一次遇到的操作(即时间上最后一次影响该学生的操作)决定其最终值,因此 ans[id] == 0 时才赋值
    • 未操作学生处理:从未被单点修改过的学生,最终值为 max(初始值, 所有整体操作的最大标准线)
    • 适用于"区间批量更新 + 单点修改"类问题,逆向处理可将 \(O(n \times m)\) 的暴力模拟优化为线性复杂度

【算法标签】

模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n, m, p[N], id[N], v[N];
int ans[N];
int main()
{
    cin >> n >> m;  // 输入n个元素和m个操作

    for (int i = 1; i <= n; i++)
    {
        cin >> p[i];  // 输入初始值
    }

    for (int i = 1; i <= m; i++)  // 输入m个操作
    {
        char op;
        cin >> op;
        if (op == 'I')  // 单点赋值操作
        {
            cin >> id[i] >> v[i];
        }
        else  // 整体更新操作
        {
            cin >> v[i];
        }
    }

    int max_line = 0;  // 记录从后往前看到的整体最大值

    // 从后往前处理操作
    for (int i = m; i >= 1; i--)
    {
        if (id[i] == 0)  // 整体操作
        {
            max_line = max(max_line, v[i]);  // 更新整体最大值
        }
        else if (ans[id[i]] == 0)  // 单点操作,且这个位置还没被赋值
        {
            ans[id[i]] = max(v[i], max_line);  // 取操作值和整体最大值的最大值
        }
    }

    // 处理未被操作过的元素
    for (int i = 1; i <= n; i++)
    {
        if (ans[i] == 0)  // 这个位置没被赋值过
        {
            ans[i] = max(p[i], max_line);  // 取初始值和整体最大值的最大值
        }
    }

    // 输出结果
    for (int i = 1; i <= n; i++)
    {
        cout << ans[i] << endl;
    }

    return 0;
}

【运行结果】

4 3
10 20 30 40
S 35
I 1 20
S 25
25
35
35
40
posted @ 2026-07-04 14:48  团爸讲算法  阅读(5)  评论(0)    收藏  举报