题(NOIP模拟赛Round #10)
题目描述:
有一张的地图,其中的地方是墙,的地方是路。有两种操作:
-
给出个地点,询问这个地点中活动空间最大的编号。若询问的位置是墙,则活动空间为;否则活动空间为询问地点通过四联通能到达的点的个数。如果有多个位置均为最大,输出给出顺序较前的那个。编号为
- 给出个地点,按照读入的顺序翻转这个地点的地形。即若原位置是墙,则该位置变为路;若原位置是路,则该位置变为墙。保证在将路变为墙时不会将一个区域分割,也不会将一个只有一格的区域填满。
保证每次操作只会询问或修改同一个位置至多一次。
————————————————我是分割线————————————————
首先我们先将一块连通的区域变为一个点,便于处理
接着我们来看修改操作:
如果一个点本来不联通,变成了连通,我们就有可能遇到两个区域连通的情况,所以我们要用并查集处理合并操作,并且合并的点不能在原图之上,要新开一个点。
但如果一个点变成墙,我们先将这个点所属的区块的压缩点的值-1,然后将这个点所属的区块设为0即可
#include<cstdio> #include<cstring> #define MN 1000005 using namespace std; char ch; int n,m,cnt,q; bool map[MN]; int d[MN],sm[MN*2],f[MN*2]; void solve(int x,int y){ if(!map[(x-1)*m+y]||d[(x-1)*m+y])return; ++sm[d[(x-1)*m+y]=cnt]; if(x>1)solve(x-1,y); if(x<n)solve(x+1,y); if(y>1)solve(x,y-1); if(y<m)solve(x,y+1); } int getfa(int x){return f[x]?f[x]=getfa(f[x]):x;} void merge(int x,int y,int xx,int yy){ if(!map[(xx-1)*m+yy]||getfa(d[(x-1)*m+y])==getfa(d[(xx-1)*m+yy]))return; sm[getfa(d[(xx-1)*m+yy])]+=sm[getfa(d[(x-1)*m+y])]; sm[getfa(d[(x-1)*m+y])]=0; f[getfa(d[(x-1)*m+y])]=getfa(d[(xx-1)*m+yy]); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%c",&ch); for(int j=1;j<=m;j++){ scanf("%c",&ch); if(ch=='*')map[(i-1)*m+j]=false; else map[(i-1)*m+j]=true; } } for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) if(map[(i-1)*m+j]&&!d[(i-1)*m+j])++cnt,solve(i,j); scanf("%d",&q); int opt,tt,x,y; for(int i=1;i<=q;i++){ scanf("%d%d",&opt,&tt); if(opt==1){ int maxn=0,maxnum=0; for(int j=1;j<=tt;j++){ scanf("%d%d",&x,&y); if(sm[getfa(d[(x-1)*m+y])]>maxn)maxn=sm[getfa(d[(x-1)*m+y])],maxnum=j; } printf("%d\n",maxnum); } else{ for(int j=1;j<=tt;j++){ scanf("%d%d",&x,&y); if(map[(x-1)*m+y]){ sm[getfa(d[(x-1)*m+y])]--; d[(x-1)*m+y]=0; map[(x-1)*m+y]=false; } else { ++sm[d[(x-1)*m+y]=++cnt]; map[(x-1)*m+y]=true; if(x>1)merge(x,y,x-1,y); if(x<n)merge(x,y,x+1,y); if(y>1)merge(x,y,x,y-1); if(y<m)merge(x,y,x,y+1); } } } } }