题解:AtCoder AT_awc0088_c Farm Harvest Festival
【题目来源】
AtCoder:C - Farm Harvest Festival
【题目描述】
Takahashi manages a vast farm.
The farm has \(N\) plots arranged in a row, numbered from \(1\) to \(N\). Plot \(i\) \((1 \leq i \leq N)\) has a fruit tree planted in it, and the amount of fruit that can be harvested from it is \(A_i\) kilograms.
During the harvest festival, Takahashi performs \(M\) harvesting operations. In the \(j\)-th \((1 \leq j \leq M)\) harvesting operation, he harvests fruit from all consecutive plots from plot \(L_j\) to plot \(R_j\).
The fruit in each plot is gone once harvested. Therefore, even if a plot is included in multiple harvesting operations, the yield obtained from that plot is only from the first time, namely \(A_i\) kilograms.
In other words, the total harvest Takahashi obtains is the sum of \(A_i\) over all plots that were targeted by at least one of the \(M\) harvesting operations. Find this total harvest.
高桥管理着一个广阔的农场。
农场有 \(N\) 块田地排成一行,编号从 \(1\) 到 \(N\)。田地 \(i\)(\(1 \leq i \leq N\))里种着一棵果树,可以从上面收获的水果量为 \(A_i\) 公斤。
在丰收节期间,高桥执行 \(M\) 次收割操作。在第 \(j\) 次(\(1 \leq j \leq M\))收割操作中,他从田地 \(L_j\) 到田地 \(R_j\) 的所有连续地块中收割水果。
每块田地的水果一旦被收割就会消失。因此,即使一块田地包含在多次收割操作中,从该田地获得的产量也只有第一次,即 \(A_i\) 公斤。
换句话说,高桥获得的总收成是所有被至少一次收割操作覆盖过的田地的 \(A_i\) 之和。求这个总收成。
【输入】
\(N\) \(M\)
\(A_1\) \(A_2\) \(\ldots\) \(A_N\)
\(L_1\) \(R_1\)
\(L_2\) \(R_2\)
\(\vdots\)
\(L_M\) \(R_M\)
- The first line contains the number of plots \(N\) and the number of harvesting operations \(M\), separated by a space.
- The second line contains the amounts of fruit harvestable from each plot \(A_1, A_2, \ldots, A_N\), separated by spaces.
- In the following \(M\) lines, the \(j\)-th line \((1 \leq j \leq M)\) contains the left endpoint \(L_j\) and right endpoint \(R_j\) of the range of plots targeted by the \(j\)-th harvesting operation, separated by a space.
【输出】
Output the total harvest of fruit that Takahashi obtains, in a single line.
【输入样例】
5 3
10 20 30 40 50
1 3
2 4
4 5
【输出样例】
150
【核心思想】
-
问题分析:给定 \(N\) 块田地的水果量 \(A_i\) 和 \(M\) 次收割区间 \([L_j, R_j]\),每块田地只收获一次。求所有被至少一次覆盖的田地的 \(A_i\) 之和。这是一个差分数组 + 前缀和问题,关键在于高效判断每块田地是否被覆盖,避免重复累加。
-
算法选择:
- 差分数组:将 \(M\) 个区间覆盖操作转化为 \(2M\) 个单点更新,实现 \(O(1)\) 区间标记
- 前缀和还原:通过一次前缀和遍历,将差分数组还原为每个位置的实际覆盖次数
- 离线处理:先记录所有操作,最后统一判断哪些位置被覆盖
-
关键步骤:
- 初始化:读取 \(N\)(田地数量)和 \(M\)(收割次数),读取水果量数组 \(A[1..N]\)
- 差分标记(处理 \(M\) 个收割区间):
- 对于每个区间 \([L_j, R_j]\):
da[L_j] += 1:在左边界标记覆盖开始da[R_j + 1] -= 1:在右边界后一个位置标记覆盖结束
- 对于每个区间 \([L_j, R_j]\):
- 前缀和还原(计算每块田地的覆盖次数):
- 遍历 \(i\) 从 \(1\) 到 \(N\):
da[i] += da[i-1] - 此时
da[i] > 0表示田地 \(i\) 被至少一次覆盖,da[i] = 0表示未被覆盖
- 遍历 \(i\) 从 \(1\) 到 \(N\):
- 统计答案:
- 遍历 \(i\) 从 \(1\) 到 \(N\):
- 若
da[i] > 0,则ans += A_i
- 若
- 遍历 \(i\) 从 \(1\) 到 \(N\):
- 输出
ans
-
时间/空间复杂度:
- 时间复杂度:\(O(N + M)\),差分标记 \(O(M)\),前缀和还原 \(O(N)\),统计答案 \(O(N)\)
- 空间复杂度:\(O(N)\),差分数组
da[1..N+1]和水果量数组 \(A[1..N]\)
-
差分数组与前缀和的核心思想:
- 区间操作降维:差分数组将区间 \([l, r]\) 的"覆盖"操作转化为
da[l] += 1和da[r+1] -= 1,实现 \(O(1)\) 区间标记 - 前缀和还原:通过前缀和将差分数组还原为实际覆盖次数,
da[i]表示田地 \(i\) 被覆盖的总次数 - 布尔化判断:本题只需判断"是否被覆盖"(
da[i] > 0),无需关心具体次数,差分后的前缀和天然满足这一需求 - 边界处理技巧:在 \(R_j + 1\) 处减 \(1\),确保只有区间内的位置受到影响,避免覆盖范围溢出
- 离线处理优势:先记录所有 \(M\) 个操作,最后统一计算,避免对每块田地重复遍历所有区间
- 适用于区间覆盖、区间更新、离线查询类问题,特别是需要判断"是否被覆盖至少一次"的场景
- 区间操作降维:差分数组将区间 \([l, r]\) 的"覆盖"操作转化为
【算法标签】
差分
【代码详解】
#include <bits/stdc++.h>
using namespace std;
// 定义长整型别名,便于处理大数据
#define int long long
// 定义数组最大容量
const int N = 200005;
// 全局变量声明
int n, m; // n: 数组长度, m: 区间操作次数
int ans; // 最终答案
int a[N]; // 原始数组
int da[N]; // 差分数组,用于高效记录区间覆盖
// 主函数入口
signed main()
{
// 读取数组长度n和区间操作次数m
cin >> n >> m;
// 读取原始数组元素
for (int i = 1; i <= n; i++)
cin >> a[i];
// 处理m次区间操作,使用差分数组标记覆盖范围
while (m--)
{
int l, r; // 区间的左右端点
cin >> l >> r;
da[l]++; // 左端点标记开始
da[r + 1]--; // 右端点后一位标记结束
}
// 通过前缀和还原每个位置被覆盖的次数
for (int i = 1; i <= n; i++)
da[i] = da[i - 1] + da[i];
// 累加所有被覆盖过的位置的元素值
for (int i = 1; i <= n; i++)
if (da[i]) // 如果该位置被覆盖过至少一次
ans += a[i];
// 输出最终结果
cout << ans << endl;
return 0;
}
【运行结果】
5 3
10 20 30 40 50
1 3
2 4
4 5
150
浙公网安备 33010602011771号