zoukankan      html  css  js  c++  java
  • 洛谷 P3690 【模板】Link Cut Tree (动态树)

    传送门
    Link/Cut Tree 板子题。就当存个模板吧。
    说一下对 LCT 的简单理解吧,对于一颗树,将其分为多个 splay,每个 splay 之间通过虚边联系,每个 splay 不重叠的维护树的一部分边。通过 access 操作,虚边变实边,实边变虚边,可以构造出一个 splay 维护从指定节点到树根的路径上的点的信息。而 makeroot 操作可以更换树的根节点。基本通过这两个操作就可以快速维护指定的一条链。至于其他的辅助函数就康康代码了。

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1e5+10;
    struct LCT{
    	int fa[N],ch[N][2],val[N],xorv[N],rev[N],tot;
    	//ident、connect都是splay的经典操作 
    	bool ident(int p,int f){return ch[f][1]==p;}
    	void connect(int p,int f,int k){ch[f][k]=p;fa[p]=f;}
    	//判断点p是否是某一颗splay的根,即p与fa[p]之间的边是否是虚边 
    	bool ntroot(int p){return ch[fa[p]][0]==p||ch[fa[p]][1]==p;}
    	//pushup常规操作 
    	void pushup(int p){xorv[p]=xorv[ch[p][0]]^xorv[ch[p][1]]^val[p];}
    	//pushdown操作是反转序列,为换树根操作铺垫。 
    	void pushdown(int p){
    		if(!rev[p])return;
    		swap(ch[p][0],ch[p][1]);
    		if(ch[p][0]) rev[ch[p][0]]^=1;
    		if(ch[p][1]) rev[ch[p][1]]^=1;
    		rev[p]=0;
    	}
    	//旋转基本操作,要判断一下f是不是根,因为虚边的处理。 
    	void rotate(int p){
    		int f=fa[p],ff=fa[f],k=ident(p,f);
    		connect(ch[p][k^1],f,k);
    		fa[p]=ff;
    		if(ntroot(f)) ch[ff][ident(f,ff)]=p;
    		connect(f,p,k^1);
    		pushup(f),pushup(p);
    	}
    	//伸展操作,将p伸展到其所处的splay的根上,注意先把路径上的标记下传。 
    	void pushall(int p){if(ntroot(p)) pushall(fa[p]);pushdown(p);}
    	void splay(int p){
    		pushall(p);
    		while(ntroot(p)){
    			int f=fa[p],ff=fa[f];
    			if(ntroot(f)) ident(p,f)^ident(f,ff)?rotate(p):rotate(f);
    			rotate(p);
    		}
    	}
    	//将从p到树根上的边全部转化为实边 
    	//其实就是把p到树根之间的所有点构成一颗splay,且不包含其他节点。splay的根是不确定的。 
    	void access(int p){for(int t=0;p;t=p,p=fa[p]) splay(p),ch[p][1]=t,pushup(p);}
    	//将p设为树根 
    	void makert(int p){access(p);splay(p);rev[p]^=1;}
    	//找到p所处的树的树根 
    	int findrt(int p){access(p);splay(p);while(ch[p][0]) p=ch[p][0];splay(p);return p;}
    	//连接点p和点q,只需特判p与q是否在同一颗树上即可 
    	void link(int p,int q){makert(p);fa[p]=q;}
    	//断开p与q的边,需判断p与q是否在同一颗树上和p与q之间是否有边。
    	//先把p设为树根,再把p、q之间的点拉到splay里,
    	//然后把q转到根,如果p此时是q的左儿子,且p没有右儿子,则p、q有边。 
    	void cut(int p,int q){makert(p);access(q);splay(q);if(ch[q][0]==p&&!ch[p][1])ch[q][0]=fa[p]=0;}
    }lct;
    
    int n,m;
    
    int main(){
    	scanf("%d%d",&n,&m);
    	lct.tot=n;
    	for(int i=1;i<=n;i++) scanf("%d",&lct.val[i]),lct.xorv[i]=lct.val[i];
    	for(int i=1,opt,x,y;i<=m;i++){
    		scanf("%d%d%d",&opt,&x,&y);
    		if(opt==0) {
    			lct.makert(x);lct.access(y);lct.splay(y);
    			printf("%d
    ",lct.xorv[y]);
    		}
    		else if(opt==1){
    			if(lct.findrt(x)!=lct.findrt(y)) lct.link(x,y);
    		}
    		else if(opt==2){
    			if(lct.findrt(x)==lct.findrt(y)) lct.cut(x,y);
    		}
    		else if(opt==3){
    			lct.access(x);lct.splay(x);lct.val[x]=y;lct.pushup(x);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    jQuery动态加载动画spin.js
    jQuery自动过滤单词插件
    基于jQuery的自定义滚动条
    jQuery纵向分类下拉菜单导航
    仿酷狗官网新闻焦点图插件
    metro扁平UI网页组件
    HTML5环形音乐播放器
    纯CSS3个性化圆形按钮登录表单
    纯CSS3绘制的黑色图标按钮组合
    纯CSS3实现iOS7扁平化图标
  • 原文地址:https://www.cnblogs.com/BakaCirno/p/12706152.html
Copyright © 2011-2022 走看看