BZOJ 2959: 长跑 lct 双联通分量 并查集 splay

http://www.lydsy.com/JudgeOnline/problem.php?id=2959

用两个并查集维护双联通分量的编号和合并。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<cmath>
  6 #include<queue>
  7 using namespace std;
  8 const int maxn=800010;
  9 int n,m;
 10 int fa[maxn]={},ch[maxn][2]={},siz[maxn]={},val[maxn]={},rev[maxn];
 11 int shu[maxn]={}; 
 12 int sta[maxn]={},tail=0;
 13 int p[maxn]={},b[maxn]={};
 14 inline int read(){
 15     int x=0;int f=1;char ch=getchar();
 16     while(ch<'0'||ch>'9'){
 17         if(ch=='-')f=-1;
 18         ch=getchar();
 19     }
 20     while('0'<=ch&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
 21     return x*f;
 22 }
 23 int findfa(int x){
 24     if(p[x]==x)return x;
 25     return p[x]=findfa(p[x]);
 26 }
 27 int bel(int x){
 28     if(b[x]==x)return x;
 29     return b[x]=bel(b[x]);
 30 }
 31 inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
 32 inline void updata(int x){ siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+val[x];}
 33 inline void rotate(int x){
 34     int y=fa[x];int fy=fa[y];
 35     int l=ch[y][0]==x?0:1;int r=l^1;
 36     if(!isroot(y)){
 37         if(ch[fy][0]==y)ch[fy][0]=x;
 38         else ch[fy][1]=x;
 39     }
 40     fa[ch[x][r]]=y;fa[y]=x;fa[x]=fy;
 41     ch[y][l]=ch[x][r]; ch[x][r]=y;
 42     updata(y);
 43 }
 44 inline void pushdown(int x){
 45     if(rev[x]){
 46         swap(ch[x][0],ch[x][1]);
 47         if(ch[x][0])rev[ch[x][0]]^=1;
 48         if(ch[x][1])rev[ch[x][1]]^=1;
 49         rev[x]=0;
 50     }
 51 }
 52 inline void splay(int x){
 53     x=bel(x);fa[x]=bel(fa[x]);
 54     int y,fy,w=x;
 55     sta[++tail]=w;
 56     while(!isroot(w)){
 57         fa[w]=bel(fa[w]);sta[++tail]=fa[w];
 58         fa[fa[w]]=bel(fa[fa[w]]);
 59         w=fa[w];
 60     }
 61     while(tail)pushdown(sta[tail--]);
 62     
 63     while(!isroot(x)){
 64         y=fa[x];fy=fa[y];
 65         if(!isroot(y)){
 66             if((ch[y][0]==x)^(ch[fy][0]==y))rotate(x);
 67             else rotate(y);
 68         }rotate(x);
 69     }updata(x);
 70 }
 71 inline void Access(int x){
 72     int y=0;
 73     while(x){
 74         x=bel(x);
 75         splay(x);ch[x][1]=y;
 76         updata(x);
 77         y=x;x=fa[x];
 78     }
 79 }
 80 inline void Reverse(int x){
 81     Access(x);splay(x);
 82     rev[x]^=1;
 83 }
 84 inline void Link(int x,int y){
 85     Reverse(x);fa[x]=y;
 86 }
 87 queue< int >q;
 88 inline void Merge(int x,int y){
 89     Reverse(y);Access(x);splay(x);
 90     int cnt=siz[x];
 91     q.push(x);
 92     while(!q.empty()){
 93         x=q.front();q.pop();
 94         if(ch[x][0])q.push(ch[x][0]);
 95         if(ch[x][1])q.push(ch[x][1]);
 96         b[bel(x)]=b[bel(y)];
 97         val[x]=siz[x]=fa[x]=ch[x][0]=ch[x][1]=0;
 98     }
 99     val[y]=siz[y]=cnt;fa[y]=0;
100 }
101 int main(){
102     //freopen("now.in","r",stdin);
103     n=read();m=read();;
104     for(int i=1;i<=n;i++){val[i]=read();shu[i]=val[i];siz[i]=val[i];}
105     for(int i=1;i<=n;i++)p[i]=i,b[i]=i;
106     int op,x,y,xx,yy;
107     for(int i=1;i<=m;i++){
108         op=read();x=read();y=read();
109         if(op==1){
110             x=bel(x);y=bel(y);
111             if(x!=y){
112                 xx=findfa(x);yy=findfa(y);
113                 if(xx==yy) Merge(x,y);
114                 else {Link(x,y);p[xx]=yy;}
115             }
116         }
117         else if(op==2){
118             xx=bel(x);
119             if(shu[x]!=y){
120                 Access(xx);splay(xx);
121                 siz[xx]+=y-shu[x];
122                 val[xx]+=y-shu[x];
123                 shu[x]=y;
124             }
125         }
126         else{
127             x=bel(x);y=bel(y);
128             if(findfa(x)!=findfa(y)){
129                 printf("-1\n");
130             }
131             else{
132                 Reverse(x);Access(y);splay(y);
133                 printf("%d\n",siz[y]);
134             }
135         }
136     }
137     return 0;
138 }
View Code

注意一下双联通分量编号的维护

posted @ 2018-03-27 07:57  鲸头鹳  阅读(170)  评论(0编辑  收藏  举报