suxxsfe

一言(ヒトコト)

bzoj2959 长跑

https://darkbzoj.tk/problem/2959

和那个bzoj4998 星球联盟比较像,都是用并查集加 lct 来维护双连通性

其实就是要处理环,因为如果无环,那么要求的值就是路径上所有点的值
而有了环,由于没有断边操作,那么就把整个环缩成一个点,用并查集来维护
还有一个并查集用来维护连通性(上一行说的是维护双连通性的)

更具体的,就是如果连边时给定两个点 \((x,y)\),它们已经连通了,就在 lct 中把它们的链 split 出来,把这些点的权值都加到 \(u,v\) 的其中一个点上,维护双连通性的并查集父亲也指向那个点
这样由于 lct 中套了一个并查集,每次要把 lct 里的点 find 一下再进行操作

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
#define N 200005
struct tr{
	tr *fa,*son[2];
	int sum,val,tag;
	tr *setfa;
}*null,dizhi[N],*pos[N];
int fa[N];
inline tr *find(tr *k){return (k->setfa==k)?k:k->setfa=find(k->setfa);}
inline int find(int k){return k==fa[k]?k:fa[k]=find(fa[k]);}
int val[N];
#define ident(tree,fa) (fa->son[1]==tree)
#define notroot(tree) (find(tree->fa)->son[0]==tree||find(tree->fa)->son[1]==tree)
#define pushup(tree) (tree->sum=tree->val+tree->son[0]->sum+tree->son[1]->sum)
inline void connect(tr *tree,tr *fa,int k){tree->fa=fa;fa->son[k]=tree;}
inline void pushdown(tr *tree){
	if(!tree->tag) return;
	tree->tag=0;
	std::swap(tree->son[0],tree->son[1]);
	tree->son[0]->tag^=1;tree->son[1]->tag^=1;
}
inline void rotate(tr *tree){
	tr *fa=find(tree->fa),*faa=find(fa->fa);
	pushdown(fa);pushdown(tree);
	int k=ident(tree,fa);
	connect(tree->son[k^1],fa,k);
	tree->fa=faa;
	if(notroot(fa)) faa->son[ident(fa,faa)]=tree;
	connect(fa,tree,k^1);
	pushup(fa);pushup(tree);
}
inline void splay(tr *tree){
	reg tr *fa,*faa;
	while(notroot(tree)){
		fa=find(tree->fa);faa=find(fa->fa);
		if(notroot(fa)) ident(fa,faa)^ident(tree,fa)?rotate(tree):rotate(fa);
		rotate(tree);
	}
}
inline void access(reg tr *x){
	for(reg tr *lastx=null;x!=null;lastx=x,x=find(x->fa)){
		pushdown(x);
		splay(x);
		x->son[1]=lastx;pushup(x);
	}
}
inline void makeroot(tr *tree){
	access(tree);
	splay(tree);tree->tag^=1;
}
int ans;
void dfs(tr *x,tr *pre){
	if(x==null) return;
	if(x!=pre) x->setfa=pre;
	pushdown(x);
	dfs(x->son[0],pre);dfs(x->son[1],pre);
}
inline void link(tr *x,tr *y){
	makeroot(x);
	x->fa=y;
}
inline void Link(int uu,int vv){
	tr *x=find(pos[uu]),*y=find(pos[vv]);
	if(x==y) return;//已经双连通
	if(find(uu)!=find(vv)){//未连通
		link(x,y);
		fa[fa[uu]]=fa[vv];
	}
	else{
		makeroot(x);
		access(y);splay(y);
		y->val=y->sum;
		dfs(y,y);
	}
}
inline void change(tr *x,int k){
	tr *findx=find(x);
	splay(findx);
	findx->val+=k;findx->sum+=k;
}
int main(){
//		std::freopen("1.in","r",stdin);
//		std::freopen("out","w",stdout);
	int n=read(),m=read();
	null=&dizhi[0];
	null->setfa=null;
	for(reg int i=1;i<=n;i++){
		pos[i]=&dizhi[i];
		dizhi[i].son[0]=dizhi[i].son[1]=dizhi[i].fa=null;
		dizhi[i].sum=dizhi[i].val=val[i]=read();
		dizhi[i].setfa=&dizhi[i];
		fa[i]=i;
	}
	int uu,vv,op;
	while(m--){
		op=read();uu=read();vv=read();
		if(op==1) Link(uu,vv);
		else if(op==2) change(pos[uu],vv-val[uu]),val[uu]=vv;
		else{
			tr *x=find(pos[uu]),*y=find(pos[vv]);
			if(find(uu)!=find(vv)) puts("-1");
			else{
				makeroot(x);
				access(y);splay(y);
				printf("%d\n",y->sum);
			}
		} 
	}
	return 0;
}
posted @ 2020-08-11 14:36  suxxsfe  阅读(116)  评论(0编辑  收藏  举报