题目链接:点击打开链接
(1)题目大意:
给定一个n*m初始化全为空白的网格mp,给出q个操作,每次操作输入x1,y1,x2,y2,将mp[x1~x2][y1~y2]包括的格子涂色,求每次操作以后以后网格中四联通空白快的数目。其中n,m<=1000,q<=1e4。
(2)解题思路:
- 求连通块,自然地想到DFS()或者BFS()去搜索,但是考虑到询问次数,如果每次操作后都搜索一次,复杂度O(nmq),显然会T;
- 反过来思考,我们将一个格子涂色以后,不容易直接判断涂色前后空白块的数目变化,但是如果我们将一个涂色的格子还原,只需要对该格子周围的四个格子进行一些判断即可知道消去颜色以后空白块的数目变化,比如原来在周围的两个格子属于不同的空白块,那么消去这个格子以后,这两个空白块就连在一起了,总数目减一,其他的情况也可以类似处理。所以我们只要对最后一次操作后的网格进行搜索,然后不断地消去历史的涂色块,就可得到每次操作后的空白块的数目,时间复杂度也可以接受。
(3)细节处理:按上述思路我们需要对一些格子进行合并连通,马上便可以想到用并查集实现,首先将最后一次操作后属于同一个空白块的格子合并在一个集合,后面的不断判断更新即可。不过要注意的一点是,如果在第一次操作和第三次操作对同一个格子进行了涂色,那么我们在反向消去的时候,只有等到消去第一次操作时才能将该格子消去(想想,很显然的),所以我这里记录了每个点被涂色时的最小操作序号。
(4)具体代码:
#include<iostream> #include<cstring> #include<string> #include<cstdio> #include<vector> #include<queue> #include<algorithm> using namespace std; const int maxn=1010; int dir[4][2]={1,0,-1,0,0,1,0,-1}; struct point{int x,y;}beg,in,out; struct node{int x1,y1,x2,y2;}Node[maxn*10]; int p[maxn*maxn],mp[maxn][maxn],n,m,q,ans[maxn*10],vis[maxn][maxn],co[maxn][maxn]; int FIND(int x){return p[x]==x?x:p[x]=FIND(p[x]);} bool check(int x,int y){return (x>=1&&x<=m)&&(y>=1&&y<=n);} void scan_d(int &num){ char ch;ch=getchar();num=0; while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')num=num*10+(ch&15),ch=getchar(); } void BFS(int x,int y,int fa){ beg.x=x;beg.y=y; queue<point>Q; Q.push(beg); vis[x][y]=1; while(!Q.empty()){ out=Q.front();Q.pop(); p[FIND(out.x*n-n+out.y)]=FIND(fa); for(int i=0;i<4;i++){ in.x=out.x+dir[i][0]; in.y=out.y+dir[i][1]; if(!check(in.x,in.y))continue; if(vis[in.x][in.y]||mp[in.x][in.y])continue; vis[in.x][in.y]=1; Q.push(in); } } } //void DFS(int x,int y,int fa){ // if(!check(x,y))return; // if(vis[x][y]||mp[x][y])return; // vis[x][y]=1; // p[FIND(x*n-n+y)]=FIND(fa); // DFS(x+1,y,FIND(fa));DFS(x-1,y,FIND(fa)); // DFS(x,y+1,FIND(fa));DFS(x,y-1,FIND(fa)); //} int main(){ // freopen("in.txt","r",stdin); scan_d(n);scan_d(m);scan_d(q); // scanf("%d%d%d",&n,&m,&q); for(int i=0;i<maxn*maxn;i++)p[i]=i; memset(co,0x3f,sizeof co); for(int i=0;i<q;i++){ scan_d(Node[i].x1);scan_d(Node[i].y1);scan_d(Node[i].x2);scan_d(Node[i].y2); // scanf("%d%d%d%d",&Node[i].x1,&Node[i].y1,&Node[i].x2,&Node[i].y2); if(Node[i].x1>Node[i].x2)swap(Node[i].x1,Node[i].x2); if(Node[i].y1>Node[i].y2)swap(Node[i].y1,Node[i].y2); for(int x=Node[i].x1;x<=Node[i].x2;x++){ for(int y=Node[i].y1;y<=Node[i].y2;y++){ mp[y][x]=1; co[y][x]=min(i,co[y][x]); } } } int cnt=0; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) if(!mp[i][j]&&!vis[i][j])BFS(i,j,FIND(i*n-n+j)),cnt++; ans[q]=cnt; for(int i=q-1;i>=0;i--){ for(int x=Node[i].x1;x<=Node[i].x2;x++){ for(int y=Node[i].y1;y<=Node[i].y2;y++){ if(co[y][x]<i)continue; int ok=1; if(ok&&check(y-1,x)&&!mp[y-1][x]){p[FIND((y-1)*n-n+x)]=FIND(y*n-n+x);ok=0;} if(ok&&check(y+1,x)&&!mp[y+1][x]){p[FIND((y+1)*n-n+x)]=FIND(y*n-n+x);ok=0;} if(ok&&check(y,x-1)&&!mp[y][x-1]){p[FIND(y*n-n+x-1)]=FIND(y*n-n+x);ok=0;} if(ok&&check(y,x+1)&&!mp[y][x+1]){p[FIND(y*n-n+x+1)]=FIND(y*n-n+x);ok=0;} if(ok){cnt++;mp[y][x]=0;continue;} if(check(y-1,x)&&!mp[y-1][x]){int yy=FIND((y-1)*n-n+x),xx=FIND(y*n-n+x);if(xx!=yy)p[yy]=xx,cnt--;} if(check(y+1,x)&&!mp[y+1][x]){int yy=FIND((y+1)*n-n+x),xx=FIND(y*n-n+x);if(xx!=yy)p[yy]=xx,cnt--;} if(check(y,x-1)&&!mp[y][x-1]){int yy=FIND(y*n-n+x-1),xx=FIND(y*n-n+x);if(xx!=yy)p[yy]=xx,cnt--;} if(check(y,x+1)&&!mp[y][x+1]){int yy=FIND(y*n-n+x+1),xx=FIND(y*n-n+x);if(xx!=yy)p[yy]=xx,cnt--;} mp[y][x]=0; } } ans[i]=cnt; } for(int i=1;i<=q;i++)printf("%d\n",ans[i]); return 0; }
哈哈,代码写得很丑啊... 。
在本校OJ上DFS爆栈了...。