题意:
分析:
动态维护图的连通性:
常见的离线做法有两种 , LCT 和 线段树分治
其中线段树分治常见的有两种:
- 操作之间独立
- 操作不独立,这种情况下大部分是按时间进行分治
对于这个题,我们要维护两种颜色的连通块个数,很容易想到并查集,但是并查集不支持删边/kk,那我们换个思路,我们把删边操作看成出现一段时间,这样只需要一个可撤销并查集
举个栗子:
当 (x,y) 这个点由 白色 变成 黑色,首先把它周围的白点的边和它断开,再把周围黑色点和它合并,维护连通块个数
代码:
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second
#define inl inline
#define reg register
using namespace std;
namespace zzc
{
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
const int maxn = 1e5+5;
int n,m,tot;
int col[205][205],frm[maxn<<3],to[maxn<<3],lst[maxn<<3],cnt[maxn];
int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
map<pii,int> pid;
struct info
{
int col,u,v;
info(){}
info(int col,int u,int v):col(col),u(u),v(v){}
};
vector<info> t[maxn<<2];
struct dsu
{
int top,cnt;
int fa[maxn],siz[maxn],st[maxn];
void init(int num)
{
for(int i=1;i<=num;i++) fa[i]=i,siz[i]=1;
}
int find(int x)
{
return fa[x]==x?x:find(fa[x]);
}
void merge(int x,int y)
{
int fx=find(x),fy=find(y);
if(fx!=fy)
{
if(siz[fx]<siz[fy]) swap(fx,fy);
st[++top]=fy;cnt++;
fa[fy]=fx;siz[fx]+=siz[fy];
}
}
void del(int num)
{
while(top>num)
{
siz[fa[st[top]]]-=siz[st[top]];
fa[st[top]]=st[top];
top--;cnt--;
}
}
}bla,whi;
inline int id(int c,int x,int y)
{
return c*n*n+(x-1)*n+y;
}
void modify(int rt,int l,int r,int ql,int qr,info x)
{
if(ql<=l&&r<=qr)
{
t[rt].pb(x);
return ;
}
int mid=(l+r)>>1;
if(ql<=mid) modify(lc,l,mid,ql,qr,x);
if(qr>mid) modify(rc,mid+1,r,ql,qr,x);
}
void add(int x,int y)
{
if(x>y) swap(x,y);
pid[mk(x,y)]=++tot;
frm[tot]=x;
to[tot]=y;
}
void link(int x,int y,int tim)
{
if(x>y) swap(x,y);
lst[pid[mk(x,y)]]=tim;
}
void cut(int c,int x,int y,int tim)
{
if(x>y) swap(x,y);
modify(1,0,m,lst[pid[mk(x,y)]],tim-1,info(c,x,y));
lst[pid[mk(x,y)]]=-1;
}
void solve(int rt,int l,int r)
{
int top1=bla.top,top2=whi.top;
for(auto i:t[rt]) i.col?bla.merge(i.u-n*n,i.v-n*n):whi.merge(i.u,i.v);
if(l==r)
{
if(l) printf("%d %d
",n*n-cnt[l]-bla.cnt,cnt[l]-whi.cnt);
bla.del(top1);whi.del(top2);
return ;
}
int mid=(l+r)>>1;
solve(lc,l,mid);solve(rc,mid+1,r);
bla.del(top1);whi.del(top2);
}
void work()
{
int x,y,c;
memset(lst,-1,sizeof(lst));memset(col,-1,sizeof(col));
n=read();bla.init(n*n);whi.init(n*n);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
col[i][j]=read();
cnt[0]+=col[i][j]==0;
if(i!=n) add(id(0,i,j),id(0,i+1,j)),add(id(1,i,j),id(1,i+1,j));
if(j!=n) add(id(0,i,j),id(0,i,j+1)),add(id(1,i,j),id(1,i,j+1));
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i!=n&&col[i][j]==col[i+1][j]) link(id(col[i][j],i,j),id(col[i+1][j],i+1,j),0);
if(j!=n&&col[i][j]==col[i][j+1]) link(id(col[i][j],i,j),id(col[i][j+1],i,j+1),0);
}
}
m=read();
for(int i=1;i<=m;i++)
{
x=read();y=read();c=col[x][y];cnt[i]=cnt[i-1];
for(int j=0;j<4;j++)
{
int tx=x+dx[j],ty=y+dy[j];
if(col[tx][ty]==c) cut(c,id(c,x,y),id(c,tx,ty),i);
}
cnt[i]-=c==0;
c^=1;col[x][y]^=1;
cnt[i]+=c==0;
for(int j=0;j<4;j++)
{
int tx=x+dx[j],ty=y+dy[j];
if(col[tx][ty]==c) link(id(c,x,y),id(c,tx,ty),i);
}
}
for(int i=1;i<=tot;i++) if(lst[i]!=-1) cut(frm[i]>n*n,frm[i],to[i],m+1);
solve(1,0,m);
}
}
int main()
{
zzc::work();
return 0;
}