1. 程式人生 > >線段樹專題-黑白棋盤 BZOJ-1453

線段樹專題-黑白棋盤 BZOJ-1453

線段樹專題-黑白棋盤

題目來源

BZOJ1453BZOJ-1453

題意

  • QQ次操作
  • 每次操作給出(x,y)(x,y),將(x,y)(x,y)個格子顏色取反
  • 每次操作後,輸出棋盤上黑白聯通塊的個數
  • n100,Q104n \le 100,Q \le 10^4

題解

顯然不能直接套線段樹,因為直接套線段樹的時間複雜度為O(n2Q)=108O(n^2Q) = 10^8

考慮使用線段樹維護並查集.

線段樹維護的是[l,r][l,r]行中黑白聯通塊的數量,其中重點維護l,rl,r兩行的並查集.

遇到合併操作的時候,如把[

l1,r1][l_1,r_1][l2,r2][l_2,r_2]做合併.

首先將各自的黑白聯通塊數量合併到[l1,r2][l_1,r_2]中去,然後再從中減去並查集合並時候造成的聯通塊減小的部分數.

首先構建一顆大小為4n4n的並查集,把l1l_1並查集放於第一層,r1r1並查集放於第二層,把l2l_2並查集放於第三層,r2r2並查集放於第四層.注意此時第一層和第二層有關係,第三層和第四層有關係.然後列舉i:[1,n]i : [1,n],打通第二層和第三層之間的關係,並更新黑白聯通塊的個數.

因為中間的二三層在做完以上操作的時候已經沒有用了,因此我們要把它們刪掉,並且將規模4

n4n的並查集縮小至2n2n.

我們需要玩一個小trictric,即把第一層的並查集的根節點全都換成第一層裡的數,把第四層並查集裡的根節點全都換成第二層裡的數.這樣的話並查集又可以縮小為2n2n了.

小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;
    }
}