题解:学而思编程 生活费
【题目来源】
学而思编程:生活费
【题目描述】
有 \(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
【核心思想】
-
问题分析:给定 \(n\) 个学生的初始生活费 \(p_i\),执行 \(m\) 次操作:单点修改(
I id v)将某学生生活费设为 \(v\),整体提升(S a)将所有低于 \(a\) 的学生提升到 \(a\)。求最终每个学生的生活费。这是一个逆向处理 + 整体最大值维护问题,关键在于识别整体操作的后效性,从后往前处理避免重复更新。 -
算法选择:
- 逆向扫描:从最后一次操作向前处理,维护当前已见的最大标准线
max_line - 懒标记思想:整体操作影响所有学生,逆向处理时只需记录一个全局最大值,单点操作时再用该值修正
- 逆向扫描:从最后一次操作向前处理,维护当前已见的最大标准线
-
关键步骤:
- 初始化:读取 \(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] == 0:ans[i] = max(p[i], max_line):初始值与最大标准线取较大值
- 若
- 遍历 \(i\) 从 \(1\) 到 \(n\):
- 输出
ans[1..n]
-
时间/空间复杂度:
- 时间复杂度:\(O(n + m)\),存储操作 \(O(m)\),逆向处理 \(O(m)\),最终处理 \(O(n)\)
- 空间复杂度:\(O(n + m)\),存储操作数组和答案数组
-
逆向处理的核心思想:
- 整体操作的后效性:
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
浙公网安备 33010602011771号