题目: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 ",cnt[1],cnt[0]); } return 0; }