线段树专题-黑白棋盘
题目来源
B Z O J − 1453 BZOJ-1453 BZOJ−1453
题意
- Q Q Q次操作
- 每次操作给出 ( x , y ) (x,y) (x,y),将 ( x , y ) (x,y) (x,y)个格子颜色取反
- 每次操作后,输出棋盘上黑白联通块的个数
- n ≤ 100 , Q ≤ 1 0 4 n \le 100,Q \le 10^4 n≤100,Q≤104
题解
显然不能直接套并查集,因为直接套并查集的时间复杂度为 O ( n 2 Q ) = 1 0 8 O(n^2Q) = 10^8 O(n2Q)=108
考虑使用线段树维护并查集.
线段树维护的是 [ l , r ] [l,r] [l,r]行中黑白联通块的数量,其中重点维护 l , r l,r l,r两行的并查集.
遇到合并操作的时候,如把 [ l 1 , r 1 ] [l_1,r_1] [l1,r1]与 [ l 2 , r 2 ] [l_2,r_2] [l2,r2]做合并.
首先将各自的黑白联通块数量合并到 [ l 1 , r 2 ] [l_1,r_2] [l1,r2]中去,然后再从中减去并查集合并时候造成的联通块减小的部分数.
首先构建一颗大小为 4 n 4n 4n的并查集,把 l 1 l_1 l1并查集放于第一层, r 1 r1 r1并查集放于第二层,把 l 2 l_2 l2并查集放于第三层, r 2 r2 r2并查集放于第四层.注意此时第一层和第二层有关系,第三层和第四层有关系.然后枚举 i : [ 1 , n ] i : [1,n] i:[1,n],打通第二层和第三层之间的关系,并更新黑白联通块的个数.
因为中间的二三层在做完以上操作的时候已经没有用了,因此我们要把它们删掉,并且将规模 4 n 4n 4n的并查集缩小至 2 n 2n 2n.
我们需要玩一个小 t r i c tric tric,即把第一层的并查集的根节点全都换成第一层里的数,把第四层并查集里的根节点全都换成第二层里的数.这样的话并查集又可以缩小为 2 n 2n 2n了.
小tric的代码
rep(i,1,n) {
tmp[findset(pa,pa[i])] = i;
tmp[findset(pa,pa[i+3*n])] = i+n;
}
rep(i,1,n) {
ns[rt].pa[i] = tmp[findset(pa,pa[i])];
ns[rt].pa[i+n] = tmp[findset(pa,pa[i+3*n])];
}
代码
#include <cstdio>
#include <iostream>
#define rep(x,a,b) for(int x = a;x <= b;++x)
#define pr(x) std::cout << #x << ":" << x << std::endl
const int N = 201;
int n,m;
int bit[N][N];
void initset(int pa[],int n) {rep(i,1,n) pa[i] = i;}
int findset(int pa[],int x) {return x == pa[x]?x:pa[x] = findset(pa,pa[x]);}
int join(int pa[],int x,int y) {
int px = findset(pa,x),py = findset(pa,y);
if(px != py) {pa[px] = py;return true;}
return false;
}
struct Node{
int pa[2*N];
int color[2];
void init(int line) {
color[0] = color[1] = 0;
initset(pa,n);
rep(i,1,n-1)
if(bit[line][i] == bit[line][i+1])
join(pa,i+1,i);
rep(i,1,n)
pa[i+n] = pa[i];
rep(i,1,n)
if(findset(pa,i) == i)
color[bit[line][i]] ++;
}
}ns[N<<2];
int pa[N<<2],tmp[N<<2];
void maintain(int rt,int l,int r){
int mid = (l + r) / 2;
int lc = rt << 1,rc = rt << 1 | 1;
rep(i,1,2*n) {
pa[i] = ns[lc].pa[i];
pa[i+2*n] = ns[rc].pa[i] + 2*n;
}
ns[rt].color[0] = ns[lc].color[0] + ns[rc].color[0];
ns[rt].color[1] = ns[lc].color[1] + ns[rc].color[1];
rep(i,1,n) {
if(bit[mid][i] == bit[mid+1][i]) {
if(join(pa,i+2*n,i+n)){
ns[rt].color[bit[mid][i]] --;
}
}
}
rep(i,1,n) {
tmp[findset(pa,pa[i])] = i;
tmp[findset(pa,pa[i+3*n])] = i+n;
}
rep(i,1,n) {
ns[rt].pa[i] = tmp[findset(pa,pa[i])];
ns[rt].pa[i+n] = tmp[findset(pa,pa[i+3*n])];
}
}
void build(int rt,int l,int r) {
if(l == r) {
ns[rt].init(l);
return ;
}
int mid = (l + r) / 2;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
maintain(rt,l,r);
}
void change(int rt,int l,int r,int pos) {
if(l == r) ns[rt].init(l);
else {
int mid = (l + r) / 2;
if(pos <= mid) change(rt<<1,l,mid,pos);
else change(rt<<1|1,mid+1,r,pos);
maintain(rt,l,r);
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin >> n;
rep(i,1,n) rep(j,1,n) {
std::cin >> bit[i][j];
}
build(1,1,n);
//std::cout << ns[1].color[0] << " " << ns[1].color[1] << std::endl;
std::cin >> m;
while(m--) {
int x,y;
std::cin >> x >> y;
bit[x][y] ^= 1;
change(1,1,n,x);
std::cout << ns[1].color[1] << " " << ns[1].color[0] << std::endl;
}
}