NCPC 2016 A-Artwork (离线+并查集+二维数组压缩
NCPC 2016 A-Artwork (离线+并查集+二维数组压缩)
题目
https://codeforces.com/gym/101550/attachments
题意
给你n,m,q三个数,代表一张n*m的图,接下来有q次询问。
q次询问给你四个数x1,y1,x2,y2 (x1=x2 or y1=y2)。表示在方格中涂黑一块区域。
然后问每一次涂黑一块区域之后,图中的联通块还有多少。
题解
首先排除dfs!太暴力了,然后这题我们可以使用并查集,把每个小方格元素都当成一个集合,然后去merge它们,最后剩下的联通块就是答案。但是每一次询问都重新刷新计算集合,这样时间复杂度会更高,所以我们可以采用离线的方式。就是从最后一步往前推。
因为二维的的图,我不会并查集,所以就把它压缩成一维了。
AC代码
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn = 1e6+5;
const int maxm = 1e4+5;
int vis[maxn],pre[maxn];
int x1[maxm],x2[maxm],y1[maxm],y2[maxm];
int n,m,q;
int sum;
int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int ans[maxm];
int Hash(int y,int x)
{
return x*n+y;
}
int find(int x)
{
if(x == pre[x]) return x;
return pre[x] = find(pre[x]);
}
void merge(int x, int y)
{
int fx = find(x), fy = find(y);
if (fx != fy)
{
pre[fx] = fy;
sum--;
}
}
void dfs(int x,int y)
{
int tx,ty;
for(int i=0;i<4;i++)
{
tx = x+dir[i][0];
ty = y+dir[i][1];
if(tx>=0&&ty>=0&&tx<n&&ty<m)
{
if(vis[Hash(tx,ty)]==0)
{
merge(Hash(tx,ty),Hash(x,y));
}
}
}
}
int main()
{
scanf("%d %d %d",&n,&m,&q);
sum = n*m;
for(int k=0;k<q;k++)
{
scanf("%d %d %d %d",&x1[k],&y1[k],&x2[k],&y2[k]);
for(int i=x1[k]-1;i<x2[k];i++)
for(int j=y1[k]-1;j<y2[k];j++)
{
if(vis[Hash(i,j)]==0)
{
sum--;
}
vis[Hash(i,j)]++;
// printf("hash(%d,%d) = %d\n",i,j,hash(i,j));
}
}
for(int i=0;i<n*m;i++) pre[i] = i;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
if(vis[Hash(i,j)]==0)
{
dfs(i,j);
}
for(int k=q-1;k>=0;k--)
{
ans[k] = sum;
for(int i=x1[k]-1;i<x2[k];i++)
for(int j=y1[k]-1;j<y2[k];j++)
{
vis[Hash(i,j)]--;
if(vis[Hash(i,j)]==0)
{
sum++;
dfs(i,j);
}
}
}
for(int i=0;i<q;i++)
printf("%d\n",ans[i]);
return 0;
}