洛谷P4121 [WC2005]双面棋盘(线段树套并查集)

传送门

先膜一下大佬->这里

据说这题正解是LCT,然而感觉还是线段树套并查集的更容易理解

我们对于行与行之间用线段树维护,每一行内用并查集暴力枚举

每一行内用并查集暴力枚举连通块这个应该容易理解,就是如果是同一个同色连通块的就用并查集连起来。那么怎么处理行与行之间的连通块嘞?

因为几行连起来可以看做一块,那么我们用$[1,n]$维护最上面一行的连通性,用$[n+1,n*2]$维护最下面一行的连通性,然后用$[n*2+1,n*4]$作为辅助

这一部分的细节还是看代码好了,写在注解里了

  1 //minamoto
  2 #include<iostream>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<algorithm>
  6 using namespace std;
  7 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
  8 char buf[1<<21],*p1=buf,*p2=buf;
  9 inline int read(){
 10     #define num ch-'0'
 11     char ch;bool flag=0;int res;
 12     while(!isdigit(ch=getc()))
 13     (ch=='-')&&(flag=true);
 14     for(res=num;isdigit(ch=getc());res=res*10+num);
 15     (flag)&&(res=-res);
 16     #undef num
 17     return res;
 18 }
 19 const int N=505;
 20 int n,m;
 21 int chess[N][N],mp[N<<2];
 22 struct node{
 23     int fa[N<<2];
 24     inline void init(){for(int i=1;i<=(n<<2);++i) fa[i]=i;}
 25     int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
 26     inline void unique(int x,int y){fa[find(x)]=find(y);}
 27     inline bool check(int x,int y){return find(x)==find(y);}
 28 }data[N<<2];
 29 int wh[N],bl[N],L[N],R[N];
 30 void pushup(int p){
 31     int mid=L[p]+R[p]>>1;
 32     wh[p]=wh[p<<1]+wh[p<<1|1];
 33     bl[p]=bl[p<<1]+bl[p<<1|1];
 34     data[p].init();
 35     for(int i=1;i<=(n<<1);++i){
 36         data[p].unique(i,data[p<<1].find(i));
 37         //1~n记录左儿子最上面一行连通性
 38         //n+1~n*2记录左儿子最下面一行的连通性 
 39         data[p].unique(i+(n<<1),data[p<<1|1].find(i)+(n<<1));
 40         //n*2+1~n*3记录右儿子最上面一行连通性
 41         //n*3+1~n*4记录左儿子最下面一行的连通性 
 42     }
 43     //枚举左右儿子交界处是否有新的连通块 
 44     for(int i=1;i<=n;++i)
 45     if(chess[mid][i]==chess[mid+1][i]){
 46         if(data[p].check(i+n,i+(n<<1))) continue;
 47         data[p].unique(i+n,i+(n<<1));
 48         wh[p]-=chess[mid][i]^1;
 49         bl[p]-=chess[mid][i];
 50     }
 51     //下面这一段就是把所有节点的父亲都赋值为下标最小的点 
 52     //顺便记录最上面一行和最下面一行的连通性 
 53     for(int i=1;i<=(n<<2);++i)
 54     data[p].find(i),mp[i]=0;
 55     for(int i=1;i<=n;++i)
 56     if(!mp[data[p].fa[i]])
 57     mp[data[p].fa[i]]=i,data[p].fa[i]=i;
 58     else data[p].fa[i]=mp[data[p].fa[i]];
 59     for(int i=n*3+1;i<=(n<<2);++i)
 60     if(!mp[data[p].fa[i]])
 61     mp[data[p].fa[i]]=i-(n<<1),data[p].fa[i]=i-(n<<1);
 62     else data[p].fa[i]=mp[data[p].fa[i]];
 63     for(int i=1;i<=n;++i) data[p].fa[i+n]=data[p].fa[i+n*3];//记录一下这一块最下面一行的连通性 
 64 }
 65 void build(int p,int l,int r){
 66     L[p]=l,R[p]=r;
 67     if(l==r){
 68         wh[p]=chess[l][1]^1;
 69         bl[p]=chess[l][1];
 70         data[p].init();
 71         data[p].unique(1+n,1);
 72         for(int i=2;i<=n;++i){
 73             data[p].unique(i+n,i);
 74             if(chess[l][i-1]==chess[l][i]) data[p].unique(i,i-1);
 75             else wh[p]+=chess[l][i]^1,bl[p]+=chess[l][i];
 76         }
 77         return;
 78     }
 79     int mid=l+r>>1;
 80     build(p<<1,l,mid),build(p<<1|1,mid+1,r);
 81     pushup(p);
 82 }
 83 void update(int x,int p){
 84     if(L[p]==R[p]){
 85         wh[p]=chess[x][1]^1;
 86         bl[p]=chess[x][1];
 87         data[p].init();
 88         data[p].unique(n+1,1);
 89         for(int i=2;i<=n;++i){
 90             data[p].unique(i+n,i);
 91             if(chess[x][i-1]==chess[x][i]) data[p].unique(i,i-1);
 92             else wh[p]+=chess[x][i]^1,bl[p]+=chess[x][i];
 93         }
 94         return;
 95     }
 96     int mid=L[p]+R[p]>>1;
 97     if(x<=mid) update(x,p<<1);
 98     else update(x,p<<1|1);
 99     pushup(p);
100 }
101 int main(){
102     //freopen("testdata.in","r",stdin);
103     n=read();
104     for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)
105     chess[i][j]=read();
106     build(1,1,n);
107     m=read();
108     while(m--){
109         int x=read(),y=read();
110         chess[x][y]^=1;
111         update(x,1);
112         printf("%d %d
",bl[1],wh[1]);
113     }
114     return 0;
115 }
原文地址:https://www.cnblogs.com/bztMinamoto/p/9600918.html