题目:http://acm.hdu.edu.cn/showproblem.php?pid=4775
题意:在一个只有左和上边界的棋盘上下围棋,如果被围死就被吃掉,求最后黑色和白色棋子的个数
我们可以利用并查集来保存连通块,同时维护这个连通块周围的空格数,如果空格数为0,则删去这个连通块,同时返还给其他连通块空格数
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define p make_pair
using namespace std;
const int N=1e4+5;
map<pair<int,int>,int> ma;
int fx[4]={0,0,1,-1};
int fy[4]={1,-1,0,0};
int f[N],fre[N];//父节点和空格数目
int cnt[2];//棋子数目
int Find(int x)
{
return f[x]==x?x:f[x]=Find(f[x]);
}
int check(int x,int y)//判断是否为空格
{
if (x==0||y==0) return 0;
if (ma.find(p(x,y))==ma.end()) return 1;
int t=ma[p(x,y)];
fre[Find(t)]--;
return 0;
}
void hb(int x,int y,int xx,int yy)//合并连通块
{
if (x==0||y==0) return;
if (ma.find(p(xx,yy))==ma.end()) return;
int t=ma[p(x,y)];
int ft=Find(t);
int tt=ma[p(xx,yy)];
int ftt=Find(tt);
if (ft!=ftt)
if (t%2==tt%2)
{
fre[ft]+=fre[ftt];
f[ftt]=ft;
}
}
void Clear(int x,int y,int col)//清除连通块
{
if (x==0||y==0) return;
if (ma.find(p(x,y))==ma.end()) return;
int t=ma[p(x,y)];
if (t%2!=col)
{
fre[Find(t)]++;
return;
}
cnt[col]--;
ma.erase(p(x,y));
for(int i=0;i<4;i++)
Clear(x+fx[i],y+fy[i],col);
}
void xc(int x,int y,int col)//判断是否需要消除
{
if (x==0||y==0) return;
if (ma.find(p(x,y))==ma.end()) return;
int t=ma[p(x,y)];
if (t%2!=col) return;
if (fre[Find(t)]==0) Clear(x,y,col);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
cnt[0]=cnt[1]=0;
ma.clear();
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
f[i]=i;
fre[i]=0;
}
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
cnt[i&1]++;
ma[p(x,y)]=i;
for(int j=0;j<4;j++)
fre[i]+=check(x+fx[j],y+fy[j]);
for(int j=0;j<4;j++)
hb(x,y,x+fx[j],y+fy[j]);
for(int j=0;j<4;j++)
xc(x+fx[j],y+fy[j],(i&1)^1);
xc(x,y,i&1);
}
printf("%d %d\n",cnt[1],cnt[0]);
}
return 0;
}