题意
对于一个 01 01 01矩阵,每次覆盖一个区域变成 1 1 1,问每个覆盖后全局的四连通块个数。
题解
最简单的办法就是暴力,但是会超时,暴力的复杂度是
O
(
n
3
)
O(n^3)
O(n3)
考虑暴力多余的操作,就是把
1
1
1变成了
1
1
1,事实上我们只需要把
0
0
0变成1。
接下来是一个比较套路的部分了,以前好像也碰到过,但是没写这种方法。
对于每一行建立一个并查集,指向的是在自己右边最近的
0
0
0。每个集合是由一个
0
0
0作为祖先,多个
1
1
1组成的。
在建立一个连通的并查集,用于联通。每次我们通过每行的并查集得到
0
0
0并变成
1
1
1,变了哪些,就把它的四周合并。当然得到
0
0
0的过程还要判断有没有超过右端点。(
[
y
1
,
y
2
]
[y1,y2]
[y1,y2])
细节还是有不少的。值得学习。
#include<bits/stdc++.h>
using namespace std;
vector<int> v;
typedef long long ll;
const int Dr[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int ans,tot,n,m;
int ID[1050][1050],Fa[1050][1050],fa[1000050];
char s[1050][1050];
int find(int *f,int x){return f[x]==x?x:(f[x]=find(f,f[x]));}
void Handle(int x,int y){
ans++;
ID[x][y]=++tot,fa[tot]=tot;
int u=find(Fa[x],y),v=find(Fa[x],y+1);
if(u!=v)Fa[x][u]=v;
for(int k=0;k<4;k++){
int tx=x+Dr[k][0],ty=y+Dr[k][1];
if(tx&&ty&&tx<=n&&ty<=m&&ID[tx][ty]){
int l=find(fa,tot),r=find(fa,ID[tx][ty]);
if(l!=r){
ans--,fa[l]=r;
}
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
for(int i=1;i<=n;i++)for(int j=1;j<=m+1;j++)Fa[i][j]=j;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(s[i][j]=='1')Handle(i,j);
}
}
int q;scanf("%d",&q);
for(int i=1;i<=q;i++){
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
for(int r=x1;r<=x2;r++){
for(int c=find(Fa[r],y1);c<=y2;c=find(Fa[r],c))
Handle(r,c);
}
printf("%d\n",ans);
}
}