题解:AtCoder AT_awc0089_a Correcting the Household Account Book
【题目来源】
AtCoder:A - Correcting the Household Account Book
【题目描述】
Takahashi is keeping a household account book for \(N\) days.
Takahashi's account balance is initially \(0\) yen. On day \(i\) \((1 \leq i \leq N)\), a transaction of \(A_i\) yen is recorded, where a positive value represents a deposit and a negative value represents a withdrawal. Each day's transaction is applied to the account balance in order (the account balance may become negative during the process). The account balance at the end of day \(N\), after all transactions have been applied, is \(A_1 + A_2 + \cdots + A_N\) yen.
However, while reviewing the account book, Takahashi noticed that some transactions were recorded incorrectly. He then performs \(Q\) correction operations in order. In the \(j\)-th operation \((1 \leq j \leq Q)\), he changes the transaction amount on day \(D_j\) to \(0\) yen. Once a day's transaction amount has been changed to \(0\), it remains \(0\) in all subsequent operations. In other words, with each operation, the number of days whose transaction amounts have been set to \(0\) increases.
After each operation, find the sum of all days' transaction amounts, that is, the account balance at the end of day \(N\).
Note that \(D_1, D_2, \ldots, D_Q\) are all distinct. In other words, the same day's transaction amount is never changed more than once.
高桥正在记录一本为期 \(N\) 天的家庭账簿。
高桥的账户余额初始为 \(0\) 日元。在第 \(i\) 天(\(1 \leq i \leq N\)),记录了一笔金额为 \(A_i\) 日元的交易,其中正值表示存款,负值表示取款。每天的交易按顺序应用到账户余额上(过程中账户余额可能变为负数)。在第 \(N\) 天结束时,所有交易应用后的账户余额为 \(A_1 + A_2 + \cdots + A_N\) 日元。
然而,在复核账簿时,高桥注意到一些交易记录有误。于是他按顺序执行 \(Q\) 次更正操作。在第 \(j\) 次操作(\(1 \leq j \leq Q\))中,他将第 \(D_j\) 天的交易金额更改为 \(0\) 日元。一旦某天的交易金额被更改为 \(0\),它在所有后续操作中都将保持为 \(0\)。换句话说,每次操作后,交易金额被设为 \(0\) 的天数会增加。
每次操作后,求所有天数的交易金额之和,即第 \(N\) 天结束时的账户余额。
注意,\(D_1, D_2, \ldots, D_Q\) 互不相同。也就是说,同一天的交易金额不会被更改超过一次。
【输入】
\(N\) \(Q\)
\(A_1\) \(A_2\) \(\cdots\) \(A_N\)
\(D_1\)
\(D_2\)
\(\vdots\)
\(D_Q\)
- The first line contains an integer \(N\) representing the number of days in the account book and an integer \(Q\) representing the number of correction operations, separated by a space.
- The second line contains \(N\) integers \(A_1, A_2, \ldots, A_N\) representing the transaction amounts for each day, separated by spaces. \(A_i\) is the transaction amount on day \(i\).
- Among the following \(Q\) lines, the \(j\)-th line (the \((2 + j)\)-th line overall) contains a single integer \(D_j\) representing the day whose transaction amount is changed to \(0\) in the \(j\)-th operation.
【输出】
Print \(Q\) lines. On the \(j\)-th line, print the sum of all days' transaction amounts (the account balance at the end of day \(N\)) after performing all operations from the \(1\)-st through the \(j\)-th.
【输入样例】
5 3
100 -50 200 -30 80
2
4
1
【输出样例】
350
380
280
【核心思想】
-
问题分析:给定 \(N\) 天的交易金额 \(A_1, A_2, \ldots, A_N\),初始总和为 \(S = \sum_{i=1}^{N} A_i\)。依次执行 \(Q\) 次操作,每次将某一天的金额 \(A_{D_j}\) 置为 \(0\)(且各 \(D_j\) 互不相同)。每次操作后需输出当前所有天的金额总和。这是一个前缀维护 + 增量更新问题,关键在于利用总和的线性性质,避免每次重新计算。
-
算法选择:
- 前缀和预计算:先一次性计算所有 \(A_i\) 的总和 \(S\)
- 增量减法更新:每次操作只需从总和中减去被置零的那一项 \(A_{D_j}\),无需遍历整个数组
-
关键步骤:
- 初始化:读取 \(N\)(天数)和 \(Q\)(操作次数)
- 计算初始总和:
- 遍历 \(i\) 从 \(1\) 到 \(N\):读取 \(A_i\) 并累加到
sum - 此时
sum = \sum_{i=1}^{N} A_i,即初始账户余额
- 遍历 \(i\) 从 \(1\) 到 \(N\):读取 \(A_i\) 并累加到
- 处理 \(Q\) 次操作:
- 对于每次操作,读取要置零的天数 \(D_j\)
sum -= A_{D_j}:从总和中减去该天的原始金额- 输出当前
sum
- 由于 \(D_1, D_2, \ldots, D_Q\) 互不相同,每个 \(A_i\) 最多被减去一次,无需额外标记
-
时间/空间复杂度:
- 时间复杂度:\(O(N + Q)\),初始求和 \(O(N)\),每次操作 \(O(1)\),共 \(Q\) 次
- 空间复杂度:\(O(N)\),存储数组 \(A[1..N]\)
-
增量更新的核心思想:
- 总和的线性分解:\(S = \sum_{i=1}^{N} A_i\),将某一项 \(A_{D_j}\) 置零等价于 \(S \leftarrow S - A_{D_j}\),操作与数组规模无关
- 离线预处理:先读入全部数据并计算初始总和,后续操作只在此基础上做增量调整
- 互异性的利用:题目保证 \(D_j\) 互不相同,因此无需维护"是否已被置零"的标记,直接减法即可
- 避免重复遍历:若每次操作后重新求和,时间复杂度为 \(O(N \times Q)\);增量更新将其优化至 \(O(N + Q)\)
- 适用于数组元素逐个失效/删除、每次查询剩余元素和的问题
【算法标签】
模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
// 定义长整型别名,便于处理大数据
#define int long long
// 定义数组最大容量
const int N = 200005;
// 全局变量声明
int n; // 数组长度
int q; // 查询次数
int a[N]; // 存储原始数组元素
// 主函数入口(使用signed避免与long long冲突)
signed main()
{
// 读取数组长度和查询次数
cin >> n >> q;
// 计算所有元素的总和
int sum = 0;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum += a[i]; // 累加每个元素到总和
}
// 处理每个查询
while (q--)
{
int x; // 要删除的元素下标
cin >> x;
sum -= a[x]; // 从总和中减去该元素
cout << sum << endl; // 输出剩余元素的和
}
return 0;
}
【运行结果】
5 3
100 -50 200 -30 80
2
350
4
380
1
280
浙公网安备 33010602011771号