BZOJ4423: [AMPPZ2013]Bytehattan
对偶图·并查集
题解:
删除一条边可以看做把两个空块连通。当删除一条边时这条边紧邻的两个空块已经连通了,那么删除这条边会导致这条边的两个顶点不连通。
仔细想想觉得非常有道理。当删除一条边时发现这条边紧邻的两个空块已经连通了,那么删除这条边后会出现一个空块连成的环,于是就把里面的点和外面的点给隔开了。
之后的事就非常简单了。将空块抠出来,然后并查集维护联通性即可。
Code:
#include <iostream>
#include <cstring>
#include <cstdio>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
const int N = 1500*1500+5;
int n,k,id[1505][1505];
struct MergeSet{
int pa[N];
void init(int sz){ for(int i=1;i<=sz;i++)pa[i]=i; }
int find(int x){ if(pa[x]!=x)pa[x]=find(pa[x]); return pa[x]; }
} ms;
void init(){
for(int i=1;i<=n-1;i++)
for(int j=1;j<=n-1;j++)
id[i][j]=(i-1)*(n-1)+j;
}
void getblock(int a,int b,char op,int &x,int &y){
if(op=='N'){ x=id[a-1][b]; y=id[a][b]; }
else{ x=id[a][b-1]; y=id[a][b]; }
}
int main(){
freopen("a.in","r",stdin);
int a1,a2,a,b1,b2,b,x,y,ans=1; char op[5];
scanf("%d%d",&n,&k);
init(); ms.init(n*n);
for(int i=1;i<=k;i++){
scanf("%d%d%s",&a1,&b1,op);
scanf("%d%d%s",&a2,&b2,op+1);
if(ans)a=a1,b=b1,op[0]=op[0];
else a=a2,b=b2,op[0]=op[1];
// D(a); D(b); D(op[0]); E;
getblock(a,b,op[0],x,y);
// D(x); D(y); E;
x=ms.find(x); y=ms.find(y);
if(x==y)ans=false;
else ans=true, ms.pa[x]=y;
printf("%s\n",ans?"TAK":"NIE");
}
}