题解:AtCoder AT_awc0080_e Paint Drop

【题目来源】

AtCoder:E - Paint Drop

【题目描述】

Takahashi is playing a paint game on a grid canvas with \(H\) rows and \(W\) columns. The cell at the \(i\)-th row from the top and the \(j\)-th column from the left is called cell \((i, j)\).

Cell \((i, j)\) has a score \(A_{i,j}\) assigned to it. Initially, no cells are painted.

Takahashi will drop paint onto the canvas \(Q\) times.

In the \(t\)-th paint drop, all cells on the canvas whose Manhattan distance from cell \((R_t, C_t)\) is at most \(D_t\) are painted.

In other words, among all cells \((i, j)\) satisfying \(1 \leq i \leq H\) and \(1 \leq j \leq W\), all cells satisfying

\(|i - R_t| + |j - C_t| \leq D_t\)

are painted.

When a cell that has not yet been painted becomes newly painted, Takahashi earns the score of that cell. No score is earned when paint reaches a cell that is already painted.

For each paint drop, find the total score newly earned by Takahashi from that paint drop.

高桥在一个有 \(H\)\(W\) 列的网格画布上玩一个涂色游戏。从上往下第 \(i\) 行、从左往右第 \(j\) 列的单元格称为单元格 \((i, j)\)

单元格 \((i, j)\) 有一个分配的分数 \(A_{i,j}\)。初始时,没有单元格被涂色。

高桥将在画布上滴下涂料 \(Q\) 次。

在第 \(t\) 次涂料滴下时,所有距离单元格 \((R_t, C_t)\) 的曼哈顿距离不超过 \(D_t\) 的单元格都会被涂色。

换句话说,在满足 \(1 \leq i \leq H\)\(1 \leq j \leq W\) 的所有单元格 \((i, j)\) 中,所有满足

\(|i - R_t| + |j - C_t| \leq D_t\)

的单元格都会被涂色。

当一个尚未被涂色的单元格被涂色时,高桥获得该单元格的分数。当涂料到达一个已经涂色的单元格时,不会获得分数。

对于每次涂料滴下,求高桥从该次滴下中新获得的分数总和。

【输入】

\(H\) \(W\)
\(A_{1,1}\) \(A_{1,2}\) \(\cdots\) \(A_{1,W}\)
\(A_{2,1}\) \(A_{2,2}\) \(\cdots\) \(A_{2,W}\)
\(\vdots\)
\(A_{H,1}\) \(A_{H,2}\) \(\cdots\) \(A_{H,W}\)
\(Q\)
\(R_1\) \(C_1\) \(D_1\)
\(R_2\) \(C_2\) \(D_2\)
\(\vdots\)
\(R_Q\) \(C_Q\) \(D_Q\)

  • The first line contains the number of rows \(H\) and the number of columns \(W\) of the canvas, separated by a space.
  • In the following \(H\) lines, the \(i\)-th line contains the scores \(A_{i,1}, A_{i,2}, \ldots, A_{i,W}\) separated by spaces.
  • The next line contains the number of paint drops \(Q\).
  • In the following \(Q\) lines, the \(t\)-th line contains the center row \(R_t\), column \(C_t\), and reach distance \(D_t\) of the \(t\)-th paint drop, separated by spaces.

【输出】

Output \(Q\) lines.

The \(t\)-th line should contain the total score newly earned by Takahashi from the \(t\)-th paint drop.

【输入样例】

3 4
1 2 3 4
5 6 7 8
9 10 11 12
4
2 2 1
1 4 0
3 1 2
2 3 7

【输出样例】

30
4
21
23

【核心思想】

  1. 问题分析:给定 \(H \times W\) 的网格,每个格子有分数 \(A_{i,j}\)。进行 \(Q\) 次涂色操作,每次以 \((R_t, C_t)\) 为中心、曼哈顿距离不超过 \(D_t\) 的菱形区域被涂色。只统计被涂色的格子的分数。这是一个并查集(Disjoint Set Union)问题,关键在于高效跳过已涂色的格子。

  2. 算法选择

    • 并查集(DSU):每行维护一个并查集 nxt[i][j],表示第 \(i\) 行中位置 \(j\) 的下一个未涂色位置
    • 路径压缩find(row, col) 函数查找下一个可访问位置,同时进行路径压缩
    • 菱形区域遍历:对于每次查询,遍历行范围 \([R_t - D_t, R_t + D_t]\),计算每行的列区间
  3. 关键步骤

    • 初始化
      • 读取 \(H\)\(W\) 和矩阵 \(A[1..H][1..W]\)
      • 初始化并查集 nxt[i][j] = j(每行每个位置指向自己)
    • 处理查询(对于每个查询 \((R_t, C_t, D_t)\)):
      • 初始化 sum = 0
      • 遍历行\(i\)\(\max(1, R_t - D_t)\)\(\min(H, R_t + D_t)\)
        • 计算剩余半径:rem = D_t - |R_t - i|
        • 计算列区间:\(j_L = \max(1, C_t - rem)\)\(j_R = \min(W, C_t + rem)\)
        • 遍历未涂色格子j = find(i, j_L)
          • \(j \leq j_R\) 时:
            • sum += A[i][j](累加分数)
            • nxt[i][j] = find(i, j + 1)(标记已涂色,指向下一位置)
            • j = find(i, j)(查找下一个未涂色位置)
      • 输出 sum
  4. 时间/空间复杂度

    • 时间复杂度:\(O(H \times W \times \alpha(W) + Q \times D_t \times \alpha(W))\),其中 \(\alpha\) 是阿克曼函数的反函数,近似常数。每个格子最多被访问一次,并查集操作近似 \(O(1)\)
    • 空间复杂度:\(O(H \times W)\),存储矩阵和并查集数组
  5. 并查集优化网格遍历的核心思想

    • 跳过已访问元素:并查集维护每个位置的下一个未访问位置,避免重复扫描已涂色格子
    • 路径压缩find 操作将路径上的所有节点直接指向根节点,加速后续查询
    • 行级并行:每行独立维护并查集,不同行之间互不影响
    • 离线处理优势:通过并查集的合并操作,动态维护未涂色格子的集合
    • 适用于网格覆盖、区间染色、去重统计类问题

【算法标签】

并查集

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 200005;
int h, w, q;  // h: 矩阵行数,w: 矩阵列数,q: 查询次数
vector<vector<int> > a;  // 矩阵
vector<vector<int> > nxt;  // 并查集数组,记录每个位置的下一个可访问位置
int ans[N];  // 存储查询结果
int find(int row, int col)  // 查找当前行col位置的下一个可访问位置
{
    if (col > w)  // 如果超出列范围
        return w + 1;  // 返回w+1表示结束
    if (nxt[row][col] != col)  // 路径压缩
        nxt[row][col] = find(row, nxt[row][col]);
    return nxt[row][col];  // 返回下一个可访问位置
}

signed main()
{
    cin >> h >> w;  // 输入矩阵行数和列数
    a.assign(h + 1, vector<int>(w + 1));  // 初始化矩阵
    nxt.assign(h + 1, vector<int>(w + 2));  // 初始化并查集数组
    for (int i = 1; i <= h; i++)  // 输入矩阵元素
        for (int j = 1; j <= w; j++)
            cin >> a[i][j];

    for (int i = 1; i <= h; i++)  // 初始化并查集数组
        for (int j = 1; j <= w + 1; j++)
            nxt[i][j] = j;
    cin >> q;  // 输入查询次数
    while (q--)  // 处理每个查询
    {
        int r, c, d;  // 查询中心(r, c)和半径d
        cin >> r >> c >> d;
        int sum = 0;  // 初始化总和
        for (int i = max(1LL, r - d); i <= min(h, r + d); i++)  // 遍历行范围
        {
            int rem = d - abs(r - i);  // 计算剩余半径
            int jL = max(1LL, c - rem);  // 计算列范围左边界
            int jR = min(w, c + rem);  // 计算列范围右边界
            int j = find(i, jL);  // 找到第一个可访问位置
            while (j <= jR)  // 遍历可访问位置
            {
                sum += a[i][j];  // 累加元素值
                nxt[i][j] = find(i, j + 1);  // 标记当前位置已访问
                j = find(i, j);  // 查找下一个可访问位置
            }
        }
        cout << sum << endl;  // 输出查询结果
    }

    return 0;
}

【运行结果】

3 4
1 2 3 4
5 6 7 8
9 10 11 12
4
2 2 1
30
1 4 0
4
3 1 2
21
2 3 7
23
posted @ 2026-06-15 09:50  团爸讲算法  阅读(7)  评论(0)    收藏  举报