【AtCoder Grand Contest 017 E】Jigsaw
欧拉路径,并查集。
题目大意:
给定 $n$ 个物体,每个物体跨过 $3$ 个横坐标,中间高度为 $h$,左右两边分别会有一部分突出(可能悬空,有高度)。
问是否存在一种摆放方案,使得每个物体中间着地,两边不悬空(要么着地要么下面有别的物体垫着)。
题解:
我们可以把每个问题左右两边看成插头。
然后对于 $A,B,C,D$,若 $C=0$,则左边的插头为 $A$,否则为 $-C$。同理若 $D=0$,则右边的插头为 $-B$,否则为 $D$。
可以发现,插头相同的可以拼起来。
然后我们把 $-2h\sim 2h$ 看成点,一个物体就代表左边的点向右边的点连出一条有向边。我们记录一个点的入度和出度即可。
然后为了满足图中的条件,这张图应当由若干条欧拉路径组成,且起点的插头大于等于 $0$,终点的插头小于等于 $0$。所以对于 $-2h\sim 0$ 的点,其出度应小于等于入度;对于 $0\sim 2h$ 的点,其入度应小于等于出度。
然后,一个弱连通块里必须存在一个入度和出度不相等的点,否则会形成自己连自己的情况。
用并查集判断即可。
Code:
#include<cstdio>
const int N=2e5+7;
int in[N],out[N],fa[N],n,h;
bool vis[N];
inline int find(int x){while(x!=fa[x])x=fa[x]=fa[fa[x]];return x;}
int main(){
scanf("%d%d",&n,&h);
for(int i=0;i<=2*h;++i)fa[i]=i;
for(int i=1;i<=n;++i){
int A,B,C,D;
scanf("%d%d%d%d",&A,&B,&C,&D);
int iL=C==0?A:-C,iR=D==0?-B:D;
++in[iL+=h],++out[iR+=h];
if(find(iL)!=find(iR))fa[fa[iL]]=fa[iR];
}
bool ok=1;
for(int i=0;i<=2*h&&ok;++i)
if(in[i]||out[i]){
if(i<=h&&in[i]>out[i])ok=0;
if(i>h&&in[i]<out[i])ok=0;
if(in[i]!=out[i])vis[find(i)]=1;
}
for(int i=0;i<=2*h&&ok;++i)
if(in[i]||out[i])if(!vis[find(i)])ok=0;
puts(ok?"YES":"NO");
return 0;
}