题解: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
【核心思想】
-
问题分析:给定 \(H \times W\) 的网格,每个格子有分数 \(A_{i,j}\)。进行 \(Q\) 次涂色操作,每次以 \((R_t, C_t)\) 为中心、曼哈顿距离不超过 \(D_t\) 的菱形区域被涂色。只统计新被涂色的格子的分数。这是一个并查集(Disjoint Set Union)问题,关键在于高效跳过已涂色的格子。
-
算法选择:
- 并查集(DSU):每行维护一个并查集
nxt[i][j],表示第 \(i\) 行中位置 \(j\) 的下一个未涂色位置 - 路径压缩:
find(row, col)函数查找下一个可访问位置,同时进行路径压缩 - 菱形区域遍历:对于每次查询,遍历行范围 \([R_t - D_t, R_t + D_t]\),计算每行的列区间
- 并查集(DSU):每行维护一个并查集
-
关键步骤:
- 初始化:
- 读取 \(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)(查找下一个未涂色位置)
- 当 \(j \leq j_R\) 时:
- 计算剩余半径:
- 输出
sum
- 初始化
- 初始化:
-
时间/空间复杂度:
- 时间复杂度:\(O(H \times W \times \alpha(W) + Q \times D_t \times \alpha(W))\),其中 \(\alpha\) 是阿克曼函数的反函数,近似常数。每个格子最多被访问一次,并查集操作近似 \(O(1)\)
- 空间复杂度:\(O(H \times W)\),存储矩阵和并查集数组
-
并查集优化网格遍历的核心思想:
- 跳过已访问元素:并查集维护每个位置的下一个未访问位置,避免重复扫描已涂色格子
- 路径压缩:
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
浙公网安备 33010602011771号