BZOJ2959: 长跑
LCT·并查集
题解:
如果没有环就是LCT裸题了,维护子树和即可。
有环的时候,不管环长得什么样(单环也好,多个环有交点、有公用边也好),只要依次为环上的边定向,一定可以做到从任意一个点开始,遍历环上所有的点,在任意一个点停下来(当然了,可能会走重复的路径)。也就是说,环跟一个点没有区别。
那么就可以缩点,用并查集维护,剩下的部分LCT。
LCT合并的时候直接选出一个代表点,把其他点打散,权值加到代表点里即可。不需要关心连向其他点的虚边,因为有特殊的access,详见代码。
注意1:在调用LCT方法需要传当前节点的指针时,一定先用并查集查一下实际应该用哪个点。特别是access,详见代码。
注意2:修改点权时不要直接x->w=w
,因为x可能代表环中的多个点,一定要使用增量。详见代码。
Code:
#include <iostream>
#include <cstring>
#include <cstdio>
#define D(x) cout<<#x<<" = "<<x<<" "
#define E cout<<endl
using namespace std;
const int N = 200005;
int n,m, cnt[N];
struct UnionFindSet{
int pa[N]; int sz;
void init(int _sz){ sz=_sz; for(int i=1;i<=sz;i++)pa[i]=i; }
int find(int x){ if(pa[x]!=x)pa[x]=find(pa[x]); return pa[x]; }
} f1, f2;
struct Node{
Node *pa, *ch[2];
int sum, w, tag;
Node(){ pa=ch[1]=ch[2]=NULL; sum=w=tag=0; }
void rev(){ tag^=1; swap(ch[0],ch[1]); }
void update(){ sum=w; if(ch[0])sum+=ch[0]->sum; if(ch[1])sum+=ch[1]->sum; }
void pushdown(){ if(tag){ tag=0; if(ch[0])ch[0]->rev(); if(ch[1])ch[1]->rev(); } }
int getwh(){ if(!pa)return -1; if(pa->ch[0]==this)return 0; if(pa->ch[1]==this)return 1; return -1; }
void setch(Node *child,int wh){ ch[wh]=child; if(child)child->pa=this; update(); }
bool isroot(){ return getwh()==-1; }
} pool[N], *s[N];
void dfs(Node *x){
printf("dfs: %d\n",x-pool);
if(x->ch[0])dfs(x->ch[0]); if(x->ch[1])dfs(x->ch[1]);
}
void rotate(Node *x){
Node *y=x->pa, *z=x->pa->pa; int wh=x->getwh();
if(y->isroot()) x->pa=z; else z->setch(x,y->getwh());
y->setch(x->ch[wh^1],wh); x->setch(y,wh^1);
}
void splay(Node *x){
Node *y; int top=0;
for(y=x;!y->isroot();y=y->pa)s[++top]=y; s[++top]=y;
for(;top;top--)s[top]->pushdown();
for(;!x->isroot();rotate(x)){
if(!x->pa->isroot())
x->getwh()==x->pa->getwh() ? rotate(x->pa) : rotate(x);
}
}
void access(Node *x){
Node *t=NULL;
while(x){ splay(x); x->setch(t,1); t=x; if(!x->pa)break; x=&pool[f2.find(x->pa-pool)]; }
}
void makeroot(Node *x){ access(x); splay(x); x->rev(); }
void link(Node *x,Node *y){ makeroot(x); x->pa=y; }
void merge(Node *x,Node *y){
// if(x!=y)printf("merge: %d %d\n",x-pool,y-pool);
int a=x-pool, b=y-pool; f2.pa[a]=b;
x->pushdown(); if(x!=y)y->w+=x->w;
if(x->ch[0])merge(x->ch[0],y); if(x->ch[1])merge(x->ch[1],y);
x->ch[0]=x->ch[1]=NULL;
}
void ins(Node *x,int w){ splay(x); x->w+=w; x->update(); }
int qsum(Node *x,Node *y){
// printf("qsum: %d %d\n",x-pool,y-pool);
makeroot(x); access(y); splay(y);
// dfs(y);
return y->sum;
}
int main(){
freopen("a.in","r",stdin);
scanf("%d%d",&n,&m);
int op,a,b; f1.init(n); f2.init(n);
for(int i=1;i<=n;i++){ scanf("%d",&cnt[i]); ins(&pool[i],cnt[i]); }
for(int i=1;i<=m;i++){
scanf("%d%d%d",&op,&a,&b);
if(op==1){
int u=f1.find(a), v=f1.find(b);
int x=f2.find(a), y=f2.find(b);
Node *xx=&pool[x], *yy=&pool[y];
if(x!=y){
if(u!=v){ link(xx,yy); f1.pa[u]=v; }
else{ makeroot(xx); access(yy); splay(yy); merge(yy,yy); }
}
}
else if(op==2){ ins(&pool[f2.find(a)],b-cnt[a]); cnt[a]=b; }
else{
if(f1.find(a)!=f1.find(b)) puts("-1");
else printf("%d\n",qsum(&pool[f2.find(a)],&pool[f2.find(b)]));
}
}
}