qduoj-598-小z的洞穴之旅(并查集维护连通块大小)

题目链接:https://qduoj.com/problem/598

题目:

小z的洞穴之旅

Description

 

小  zzz  同学在某个闲暇的周末决定去野外探险一波,结果在丛林深处中误打误撞进入了一个神秘的洞穴,虽然洞穴中光线昏暗,但小  zzz  凭借其敏锐的眼力立刻辨认出这是一个迷宫状洞穴,并且他还发现了一个现象:该洞穴中时不时会有一个墙块自行坍塌,每个墙体坍塌后其所在单元格即变为空地,其坍塌过程中所产生的尘土也会随之传到该墙体相连的各个空地处,于是他很好奇,对于每一次墙块的坍塌,所产生的尘土会遍及到多大的空白区域?

Input

 

第一行包含两个正整数n、mn、mn、m , 即迷宫区域的长度与宽度,1&lt;=n,m&lt;=50001&lt;=n,m&lt;=50001<=n,m<=5000

接下来nnn行,每行包含mmm个字符,表示迷宫每一行的构造; "."表示空白区域,"#"表示墙体;

接下来一行为一个整数qqq, 表示小z依次看到的q次坍塌现象(即q次询问), q&lt;=1000000q&lt;=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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值