GYM 101550 A.Artwork(并查集)

38 篇文章 0 订阅

Description

给出一个 n×m n × m 的网格,初始状态所有格子均为白色, q q 次操作,每次操作会把该网格的几个格子涂黑,问每次操作后白色格子的连通分支数,两个白块相邻当且仅当它们共边

Input

第一行三个整数n,m,q表示网格行列数和操作数,之后 q q 行每行输入四个整数x1,y1,x2,y2,表示将所有满足 x1xx2,y1yy2 x 1 ≤ x ≤ x 2 , y 1 ≤ y ≤ y 2 的格子 (x,y) ( x , y ) 涂黑,且输入保证 x1=x2 x 1 = x 2 y1=y2 y 1 = y 2

(1n,m1000,1q104,1x1x2n,1y1y2m) ( 1 ≤ n , m ≤ 1000 , 1 ≤ q ≤ 10 4 , 1 ≤ x 1 ≤ x 2 ≤ n , 1 ≤ y 1 ≤ y 2 ≤ m )

Output

每次操作后,输出白块连通分支数

Sample Input

4 6 5
2 2 2 6
1 3 4 3
2 5 3 5
4 6 4 6
1 6 4 6

Sample Output

1
3
3
4
3

Solution

离线,从后往前看即为把一些黑块变成白块,该白块会把其上下左右的白块所属连通分支合并,用并查集维护即可

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
#define y1 yy1
#define y2 yy2
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=1005,maxq=10005;
int n,m,num,f[maxn*maxn],a[maxn][maxn],q,x1[maxq],y1[maxq],x2[maxq],y2[maxq],ans[maxq];
int dx[]={-1,0,1,0};
int dy[]={0,-1,0,1};
void init(int n)
{
    for(int i=1;i<=n;i++)f[i]=i;
}
int find(int x)
{
    if(f[x]==x)return x;
    return f[x]=find(f[x]);
}
void unite(int x,int y)
{
    x=find(x),y=find(y);
    if(x==y)return ;
    num--;
    f[x]=y;
}
int ID(int x,int y)
{
    return (x-1)*m+y;
}
int main()
{
    while(~scanf("%d%d%d",&n,&m,&q))
    {
        init(n*m);
        memset(a,0,sizeof(a));
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d%d%d",&x1[i],&y1[i],&x2[i],&y2[i]);
            if(x1[i]==x2[i])
                for(int j=y1[i];j<=y2[i];j++)a[x1[i]][j]++;
            else 
                for(int j=x1[i];j<=x2[i];j++)a[j][y1[i]]++;
        }
        num=n*m;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                if(!a[i][j])
                    for(int k=0;k<4;k++)
                    {
                        int ii=i+dx[k],jj=j+dy[k];
                        if(ii>=1&&ii<=n&&jj>=1&&jj<=m&&!a[ii][jj])
                        {
                            unite(ID(i,j),ID(ii,jj));
                        }
                    }
                else num--;
        for(int i=q;i>=1;i--)
        {
            ans[i]=num;
            if(x1[i]==x2[i])
                for(int j=y1[i];j<=y2[i];j++)
                {
                    a[x1[i]][j]--;
                    if(a[x1[i]][j])continue;
                    num++;
                    for(int k=0;k<4;k++)
                    {
                        int ii=x1[i]+dx[k],jj=j+dy[k];
                        if(ii>=1&&ii<=n&&jj>=1&&jj<=m&&!a[ii][jj])
                            unite(ID(x1[i],j),ID(ii,jj));
                    }
                }
            else 
                for(int j=x1[i];j<=x2[i];j++)
                {
                    a[j][y1[i]]--;
                    if(a[j][y1[i]])continue;
                    num++;
                    for(int k=0;k<4;k++)
                    {
                        int ii=j+dx[k],jj=y1[i]+dy[k];
                        if(ii>=1&&ii<=n&&jj>=1&&jj<=m&&!a[ii][jj])
                            unite(ID(j,y1[i]),ID(ii,jj));
                    }
                }
        }
        for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值