浅谈树状数组与线段树:https://www.cnblogs.com/AKMer/p/9946944.html
题目传送门:https://lydsy.com/JudgeOnline/problem.php?id=1453
线段树维护行区间,每个结点只记录这个区间最上面一行和最下面一行每个格子在当前区间内的并查集情况,然后区间(update)的时候暴力合并就行了。因为除去最上面一行和最下面一行以外的格子都没有用,所以要保证并查集的代表元素是最上面一行或者最下面一行的。
时间复杂度:(O(mnlogn))
空间复杂度:(O(n^2))
代码如下:
#include <cstdio>
using namespace std;
int n,m;
int map[205][205];
int fa[805],tmp[805];
int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
}
int find(int x) {
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
struct segment_tree {
struct tree_node {
int cnt[2],f[405];
void init(int x) {
f[1]=f[n+1]=1;int top=1;
cnt[map[x][1]]=1,cnt[map[x][1]^1]=0;
for(int i=2;i<=n;i++) {
if(map[x][i]!=map[x][top])
cnt[map[x][i]]++,top=i;
f[i]=f[i+n]=top;
}
}
}tree[805];
void update(int p,int x) {
int ls=p<<1,rs=p<<1|1;
for(int i=1;i<=n<<1;i++) {
fa[i]=tree[ls].f[i];
fa[i+(n<<1)]=tree[rs].f[i]+(n<<1);
}
tree[p].cnt[0]=tree[ls].cnt[0]+tree[rs].cnt[0];
tree[p].cnt[1]=tree[ls].cnt[1]+tree[rs].cnt[1];
for(int i=1;i<=n;i++)
if(map[x][i]==map[x+1][i]&&find(i+n)!=find(i+(n<<1)))
tree[p].cnt[map[x][i]]--,fa[fa[i+n]]=fa[i+(n<<1)];
for(int i=1;i<=n<<2;i++) {
find(i);
if(i<=n)tmp[fa[i]]=i;
if(i>n*3)tmp[fa[i]]=i-n*2;
}
for(int i=1;i<=n;i++) {
tree[p].f[i]=tmp[fa[i]];
tree[p].f[i+n]=tmp[fa[i+3*n]];
}
}
void build(int p,int l,int r) {
if(l==r) {tree[p].init(l);return;}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
update(p,mid);
}
void change(int p,int l,int r,int pos) {
if(l==r) {tree[p].init(l);return;}
int mid=(l+r)>>1;
if(pos<=mid)change(p<<1,l,mid,pos);
else change(p<<1|1,mid+1,r,pos);
update(p,mid);
}
void work() {
int x=read(),y=read();
map[x][y]^=1;
change(1,1,n,x);
printf("%d %d
",tree[1].cnt[1],tree[1].cnt[0]);
}
}T;
int main() {
n=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
map[i][j]=read();
T.build(1,1,n);m=read();
for(int i=1;i<=m;i++)T.work();
return 0;
}