题解 DTOJ #2498.大步小步(babystep)

本文介绍了一个关于网格图的趣味算法问题,涉及到并查集数据结构的应用。Baby在网格图中炸掉边,玩家需判断两点是否仍连通。文章详细解析了算法思路,包括如何利用并查集维护连通性,以及代码实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

欢迎访问 My Luogu Space

题目描述

从前有一个Baby。

从前还有一个网格图。

Baby喜欢爆炸。

Baby偶尔会炸掉网格图中的一条边 ( u , v ) (u,v) (u,v)。之后他会尝试从 u u u 走到 v v v

如果他成功地从 u u u 走到 v v v ,他会很高兴;否则他会找人打架。

从第二次爆炸开始,根据Baby此时心情的不同,Baby会炸掉不同的边。

你被要求编写一个程序,对于每次爆炸,给出此时Baby是否还能从 u u u v v v

输入输出格式

输入格式:

第一行,一个整数 R R R,代表网格图是 R ∗ R R*R RR 的。

第二行,一个整数 N N N,代表操作数。

第三行,四个整数 x 1 x1 x1 , y 1 y1 y1 , x 2 x2 x2 , y 2 y2 y2,描述了第一次爆炸的边。

以下 N − 1 N-1 N1 行,每行四个整数 x 1 x1 x1 , y 1 y1 y1 , x 2 x2 x2 , y 2 y2 y2 , x 1 ’ x1’ x1 , y 1 ’ y1’ y1 , x 2 ’ x2’ x2 , y 2 ’ y2’ y2,分别描述Baby高兴时炸的边和想找人打架时炸的边。

输出格式:

输出 N N N 行,如果本次爆炸后Baby高兴,输出一行 “HAHA”,否则输出一行“DAJIA”。

输入输出样例

输入样例:

2
2
1 1 1 2
1 1 2 1 1 2 2 2

输出样例:

HAHA
DAJIA


标签

并查集。


分析

通过转换连通的对象反向思考,利用并查集对进行答案的判断。

基本想法

题目描述的是一个 R ∗ R R*R RR 网格图,每进行一次计算都会少掉其中的一条边。

可以发现,每次少掉一条边,这条边左右的两个格子便会被连通。因此可以利用并查集判断每次删掉当前边之前这条边左右的两个格子是否有被连通:

  1. 被连通:则这条边对应的两个点周围被一圈连通的空格围住,除了当前边无法再通过其他边连通这两个点。
  2. 未被联通:则周围不是联通的空格,可以找到当前边除外的另一条边连通这两个点。

综上,判断并输出即可。

改进

利用并查集可以方便地维护连通块。

将所有的空格标上序号 1 1 1 ~ ( r − 1 ) 2 (r-1)^2 (r1)2 F a [ x ] Fa[x] Fa[x] 表示第 x x x 个空格的父节点。通过函数 G e t F a ( x ) GetFa(x) GetFa(x) 来找到这个节点的父节点(加入路径压缩)。

特别地,令所有超过网格图之外的空格的序号为 0 0 0(网格外的所有空间都可以算作同一个格子)


【代码】

[C++]

#include <bits/stdc++.h>
#define get(a) a=getchar()
#define isd(a) isdigit(a)
#define il inline
#define y1 Y1
#define cs const
using namespace std;
const int MAXR = 500+5;
 
int n, r, x1, y1, x2, y2, d;
int Fa[MAXR*MAXR];
 
int Rd(){
    char c;
    while(!isd(get(c)));  int a=c^48;
    while(isd(get(c))) a = (a<<1)+(a<<3)+(c^48);
    return a;
}
il int No(cs int &x, cs int &y){return x<1||y<1||x>=r||y>=r?0:(y-1)*(r-1)+x;}  //返回空格的编号
il int GetFa(int x){return Fa[x]==x?x:Fa[x]=GetFa(Fa[x]);}
il void R(){x1=Rd(); y1=Rd(); x2=Rd(); y2=Rd();}  //有用数据
il void D(){for(int i=4; i--; d=Rd());}  //无用数据
bool Happy(){
    bool can=0;
    int fa1, fa2;
    if(x1 > x2) swap(x1, x2);  //将情况转换为(1)点始终在左或者下
    if(y1 < y2) swap(y1, y2);
    if(x1 == x2) fa1=GetFa(No(x1-1, y1-1)), fa2=GetFa(No(x2, y2));
    else fa1=GetFa(No(x1, y1)), fa2=GetFa(No(x2-1, y2-1));
    if(fa1^fa2) can = 1;
    Fa[fa1] = Fa[fa2]; 
    puts(can?"HAHA":"DAJIA");
    return can;
}
int main(){
    r=Rd(), n=Rd(), x1=Rd(), y1=Rd(), x2=Rd(), y2=Rd();
    for(int i=1; i<=r*r; i++) Fa[i] = i;
    while(n--){
        if(Happy() && n) R(), D();
        else if(n) D(), R();
    }
    return 0;
}

补充

多注意网格图上的点的坐标和空格的坐标不完全一样,注意找到对应关系。


Over

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值