题解 DTOJ #2498.大步小步(babystep)
欢迎访问 My Luogu Space。
【题目描述】
从前有一个Baby。
从前还有一个网格图。
Baby喜欢爆炸。
Baby偶尔会炸掉网格图中的一条边 \((u,v)\)。之后他会尝试从 \(u\) 走到 \(v\)。
如果他成功地从 \(u\) 走到 \(v\) ,他会很高兴;否则他会找人打架。
从第二次爆炸开始,根据Baby此时心情的不同,Baby会炸掉不同的边。
你被要求编写一个程序,对于每次爆炸,给出此时Baby是否还能从 \(u\) 到 \(v\) 。
【输入输出格式】
输入格式:
第一行,一个整数 \(R\),代表网格图是 \(R*R\) 的。
第二行,一个整数 \(N\),代表操作数。
第三行,四个整数 \(x1\) , \(y1\) , \(x2\) , \(y2\),描述了第一次爆炸的边。
以下 \(N-1\) 行,每行四个整数 \(x1\) , \(y1\) , \(x2\) , \(y2\) , \(x1’\) , \(y1’\) , \(x2’\) , \(y2’\),分别描述Baby高兴时炸的边和想找人打架时炸的边。
输出格式:
输出 \(N\) 行,如果本次爆炸后Baby高兴,输出一行 “HAHA”,否则输出一行“DAJIA”。
【输入输出样例】
输入样例:
2
2
1 1 1 2
1 1 2 1 1 2 2 2
输出样例:
HAHA
DAJIA
【标签】
并查集。
【分析】
通过转换连通的对象反向思考,利用并查集对进行答案的判断。
基本想法:
题目描述的是一个 \(R*R\) 网格图,每进行一次计算都会少掉其中的一条边。
可以发现,每次少掉一条边,这条边左右的两个格子便会被连通。因此可以利用并查集判断每次删掉当前边之前这条边左右的两个格子是否有被连通:
- 被连通:则这条边对应的两个点周围被一圈连通的空格围住,除了当前边无法再通过其他边连通这两个点。
- 未被联通:则周围不是联通的空格,可以找到当前边除外的另一条边连通这两个点。
综上,判断并输出即可。
改进:
利用并查集可以方便地维护连通块。
将所有的空格标上序号 \(1\) ~ \((r-1)^2\),\(Fa[x]\) 表示第 \(x\) 个空格的父节点。通过函数 \(GetFa(x)\) 来找到这个节点的父节点(加入路径压缩)。
特别地,令所有超过网格图之外的空格的序号为 \(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;
}
【补充】
多注意网格图上的点的坐标和空格的坐标不完全一样,注意找到对应关系。