题目链接:https://qduoj.com/problem/598
题目:
小z的洞穴之旅
Description
小 zzz 同学在某个闲暇的周末决定去野外探险一波,结果在丛林深处中误打误撞进入了一个神秘的洞穴,虽然洞穴中光线昏暗,但小 zzz 凭借其敏锐的眼力立刻辨认出这是一个迷宫状洞穴,并且他还发现了一个现象:该洞穴中时不时会有一个墙块自行坍塌,每个墙体坍塌后其所在单元格即变为空地,其坍塌过程中所产生的尘土也会随之传到该墙体相连的各个空地处,于是他很好奇,对于每一次墙块的坍塌,所产生的尘土会遍及到多大的空白区域?
Input
第一行包含两个正整数n、mn、mn、m , 即迷宫区域的长度与宽度,1<=n,m<=50001<=n,m<=50001<=n,m<=5000
接下来nnn行,每行包含mmm个字符,表示迷宫每一行的构造; "."表示空白区域,"#"表示墙体;
接下来一行为一个整数qqq, 表示小z依次看到的q次坍塌现象(即q次询问), q<=1000000q<=1000000q<=1000000
接下来qqq行,每行一对正整数 x,yx , yx,y, 表示这一次小z看到了第x行第y列的墙体发生了坍塌(保证当前状态下(x,y)(x,y)(x,y)一定是一个未坍塌的墙体)
Output
对于每次询问(每一次的坍塌),输出其坍塌所产生的尘土会遍及到多大的空白区域(假定每个单元格体积为1),每次询问的输出都各占一行。
Sample Input 1
4 5
#.###
#####
##..#
#####
3
1 5
1 1
3 5
Sample Output 1
1
2
3
Sample Input 2
3 3
..#
##.
##.
3
1 3
3 2
3 1
Sample Output 2
5
6
7
Hint
超出边界的部分一律看作墙体。
解题思路:并查集维护连通块大小即可,具体见代码。
AC代码:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <set>
#include <string>
#include <vector>
using namespace std;
typedef long long ll;
const int MAX = 5e3 + 5;
int n, m, fa[MAX * MAX], num[MAX * MAX];
char mp[MAX][MAX];
struct Node {
int x, y;
};
queue<Node> q;
// getid()将二维点(x,y)映射为一维的数值
inline int getid(int x, int y) { return (x - 1) * m + y; }
//并查集操作
int getf(int x) { return fa[x] == x ? x : fa[x] = getf(fa[x]); }
void merge(int u, int v) {
int tu = getf(u), tv = getf(v);
if (tu == tv) return;
fa[tv] = tu;
num[tu] += num[tv];
num[tv] = 0;
}
//预处理部分
void bfs(int x, int y, int rt) {
while (q.size()) q.pop();
int i, j, tmp;
Node h;
int de[4][2] = {1, 0, 0, 1, -1, 0, 0, -1}, tx, ty;
q.push(Node{x, y});
while (q.size()) {
h = q.front();
q.pop();
for (i = 0; i < 4; i++) {
tx = h.x + de[i][0];
ty = h.y + de[i][1];
if (tx <= 0 || ty <= 0 || tx > n || ty > m || mp[tx][ty] == '#')
continue;
tmp = getid(tx, ty);
if (tx == x && ty == y || fa[tmp] != tmp) continue;
merge(rt, tmp);
q.push(Node{tx, ty});
}
}
}
void init() { //先把初始的图进行并查集的合并
int i, j, tmp;
for (i = 1; i <= n; i++) {
for (j = 1; j <= m; j++) {
tmp = getid(i, j);
fa[tmp] = tmp;
num[tmp] = 1;
}
}
for (i = 1; i <= n; i++) {
for (j = 1; j <= m; j++) {
tmp = getid(i, j);
// fa[tmp]!=tmp即表示该块已被遍历过
if (mp[i][j] == '#' || fa[tmp] != tmp) continue;
bfs(i, j, tmp);
}
}
}
int main() {
int t, que, i, j, k, id, tid, x, y;
int de[4][2] = {1, 0, 0, 1, -1, 0, 0, -1}, tx, ty;
cin >> n >> m;
for (i = 1; i <= n; i++) scanf("%s", mp[i] + 1);
init();
cin >> que;
for (; que; que--) { //处理询问
scanf("%d%d", &x, &y);
id = getid(x, y);
for (i = 0; i < 4; i++) { //尝试将(x,y)点与周围上下左右四个区域合并
tx = x + de[i][0];
ty = y + de[i][1];
if (tx <= 0 || ty <= 0 || tx > n || ty > m || mp[tx][ty] == '#')
continue;
tid = getf(getid(tx, ty));
merge(tid, id);
}
mp[x][y] = '.'; //最后还需要维护原始的mp[][]
printf("%d\n", num[getf(id)]); //返回当前点(x,y)所在的连通块的大小
}
return 0;
}