题意:初始n*m个白块.一个白块可以走到相邻4个白块中.Q次操作.某次横着或者竖着将若干个白块变为黑块.
n,m<=1e3, Q<=1e4.每次操作过后,问白块的连通分量个数为?
每次若干个白块变为黑色. 相当于在图中删除若干个点以及它的边.然后在求连通分量.O(nmQ)TLE.
正难则反.:从后往前.删点变为加点.每次加入一个点u,看点u相邻的4个连通分量是否和u所在的联通分量相同,若不同则用并查集相连即可. O(Q+(n*m*4)).
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> ii;
const int N=2e3+5,M=1e4+5;
int n,m,Q,dx[]={-1,1,0,0},dy[]={0,0,-1,1};
bool a[N][N],vis[N][N];
vector<ii> q[M];
int fa[N*N],cn=0,ans[M];
void dfs(int x,int y,int cn){
fa[(x-1)*m+y]=cn;
vis[x][y]=true;
for(int i=0;i<4;i++){
int nx=x+dx[i],ny=y+dy[i];
if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&!vis[nx][ny]&&!a[nx][ny])
dfs(nx,ny,cn);
}
}
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
cin>>n>>m>>Q;
int sx,sy,ex,ey;
for(int i=1;i<=Q;i++){
cin>>sx>>sy>>ex>>ey;
if(sx==ex){
for(int y=sy;y<=ey;y++){
if(a[sx][y]) continue;
a[sx][y]=true;
q[i].push_back(ii(sx,y));
}
}
if(sy==ey){
for(int x=sx;x<=ex;x++){
if(a[x][sy]) continue;
a[x][sy]=true;
q[i].push_back(ii(x,sy));
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(vis[i][j]||a[i][j]){
if(a[i][j]) fa[(i-1)*m+j]=(i-1)*m+j;
continue;
}
cn++;
dfs(i,j,(i-1)*m+j);
}
}
ans[Q]=cn;
for(int i=Q;i>=2;i--){
for(int j=0;j<q[i].size();j++){
int x=q[i][j].first,y=q[i][j].second,black=0;
bool first=true;
a[x][y]=false;
for(int k=0;k<4;k++){
int nx=x+dx[k],ny=y+dy[k];
if(nx>=1&&nx<=n&&ny>=1&&ny<=m){
if(a[nx][ny]){
black++;
continue;
}
int fx=find((x-1)*m+y),fy=find((nx-1)*m+ny);
if(fx!=fy){
fa[fx]=fy;
if(first==false) cn--;
first=false;
}
}
else black++;
}
if(black==4) cn++;
// cout<<i<<' '<<j<<' '<<black<<'\n';
}
ans[i-1]=cn;
}
for(int i=1;i<=Q;i++) cout<<ans[i]<<'\n';
return 0;
}