线段树专题-黑白棋盘 BZOJ-1453
線段樹專題-黑白棋盤
題目來源
BZOJ?1453BZOJ-1453BZOJ?1453
題意
- QQQ次操作
- 每次操作給出(x,y)(x,y)(x,y),將(x,y)(x,y)(x,y)個格子顏色取反
- 每次操作后,輸出棋盤上黑白聯通塊的個數
- n≤100,Q≤104n \le 100,Q \le 10^4n≤100,Q≤104
題解
顯然不能直接套并查集,因為直接套并查集的時間復雜度為O(n2Q)=108O(n^2Q) = 10^8O(n2Q)=108
考慮使用線段樹維護并查集.
線段樹維護的是[l,r][l,r][l,r]行中黑白聯通塊的數量,其中重點維護l,rl,rl,r兩行的并查集.
遇到合并操作的時候,如把[l1,r1][l_1,r_1][l1?,r1?]與[l2,r2][l_2,r_2][l2?,r2?]做合并.
首先將各自的黑白聯通塊數量合并到[l1,r2][l_1,r_2][l1?,r2?]中去,然后再從中減去并查集合并時候造成的聯通塊減小的部分數.
首先構建一顆大小為4n4n4n的并查集,把l1l_1l1?并查集放于第一層,r1r1r1并查集放于第二層,把l2l_2l2?并查集放于第三層,r2r2r2并查集放于第四層.注意此時第一層和第二層有關系,第三層和第四層有關系.然后枚舉i:[1,n]i : [1,n]i:[1,n],打通第二層和第三層之間的關系,并更新黑白聯通塊的個數.
因為中間的二三層在做完以上操作的時候已經沒有用了,因此我們要把它們刪掉,并且將規模4n4n4n的并查集縮小至2n2n2n.
我們需要玩一個小trictrictric,即把第一層的并查集的根節點全都換成第一層里的數,把第四層并查集里的根節點全都換成第二層里的數.這樣的話并查集又可以縮小為2n2n2n了.
小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;} }總結
以上是生活随笔為你收集整理的线段树专题-黑白棋盘 BZOJ-1453的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2015电脑配置推荐配置文件(2015电
- 下一篇: ACM-ICPC中博弈论的一些小小总结