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

【核心思想】

  1. 问题分析:给定 \(N\) 块田地的水果量 \(A_i\)\(M\) 次收割区间 \([L_j, R_j]\),每块田地只收获一次。求所有被至少一次覆盖的田地的 \(A_i\) 之和。这是一个差分数组 + 前缀和问题,关键在于高效判断每块田地是否被覆盖,避免重复累加。

  2. 算法选择

    • 差分数组:将 \(M\) 个区间覆盖操作转化为 \(2M\) 个单点更新,实现 \(O(1)\) 区间标记
    • 前缀和还原:通过一次前缀和遍历,将差分数组还原为每个位置的实际覆盖次数
    • 离线处理:先记录所有操作,最后统一判断哪些位置被覆盖
  3. 关键步骤

    • 初始化:读取 \(N\)(田地数量)和 \(M\)(收割次数),读取水果量数组 \(A[1..N]\)
    • 差分标记(处理 \(M\) 个收割区间):
      • 对于每个区间 \([L_j, R_j]\)
        • da[L_j] += 1:在左边界标记覆盖开始
        • da[R_j + 1] -= 1:在右边界后一个位置标记覆盖结束
    • 前缀和还原(计算每块田地的覆盖次数):
      • 遍历 \(i\)\(1\)\(N\)da[i] += da[i-1]
      • 此时 da[i] > 0 表示田地 \(i\) 被至少一次覆盖,da[i] = 0 表示未被覆盖
    • 统计答案
      • 遍历 \(i\)\(1\)\(N\)
        • da[i] > 0,则 ans += A_i
    • 输出 ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N + M)\),差分标记 \(O(M)\),前缀和还原 \(O(N)\),统计答案 \(O(N)\)
    • 空间复杂度:\(O(N)\),差分数组 da[1..N+1] 和水果量数组 \(A[1..N]\)
  5. 差分数组与前缀和的核心思想

    • 区间操作降维:差分数组将区间 \([l, r]\) 的"覆盖"操作转化为 da[l] += 1da[r+1] -= 1,实现 \(O(1)\) 区间标记
    • 前缀和还原:通过前缀和将差分数组还原为实际覆盖次数,da[i] 表示田地 \(i\) 被覆盖的总次数
    • 布尔化判断:本题只需判断"是否被覆盖"(da[i] > 0),无需关心具体次数,差分后的前缀和天然满足这一需求
    • 边界处理技巧:在 \(R_j + 1\) 处减 \(1\),确保只有区间内的位置受到影响,避免覆盖范围溢出
    • 离线处理优势:先记录所有 \(M\) 个操作,最后统一计算,避免对每块田地重复遍历所有区间
    • 适用于区间覆盖、区间更新、离线查询类问题,特别是需要判断"是否被覆盖至少一次"的场景

【算法标签】

差分

【代码详解】

#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
posted @ 2026-07-04 09:37  团爸讲算法  阅读(7)  评论(0)    收藏  举报