Link Cut Tree
来源:互联网 发布:mysql 等于符号 编辑:程序博客网 时间:2024/06/05 14:35
动态树是一类要求维护森林的连通性的题的总称,
这类问题要求维护某个点到根的某些数据,支持树的切分,合并,
以及对子树的某些操作
其中解决这一问题的某些简化版(不包括对子树的操作)的基础数据结构
就是LCT(link-cut tree)
定义:
首先来定义一些量:
access(x)(或者叫expose(x)):表示访问X点
Preferred child(偏爱子节点):如果最后被访问的点在X的儿子P节点的子树中,那么称P为X的Preferred child,如果一个点被访问,他的Preferred child为null(即没有)。
Preferred edge(偏爱边):每个点到自己的Preferred child的边被称为Preferred edge。
Preferred path(偏爱路径):由Preferred edge组成的不可延伸的路径称为Preferred path。
这样我们可以发现一些比较显然的性质,
每个点在且仅在一条Preferred path上,
也就是所有的Preferred path包含了这棵树上的所有的点,
这样一颗树就可以由一些Preferred path来表示(类似于轻重链剖分中的重链),
我们用splay来维护每个条Preferred path,关键字为深度,
也就是每棵splay中的点左子树的深度都比当前点小,右节点的深度都比当前节点的深度大
这样的每棵splay我们称为Auxiliary tree(辅助树),
每个Auxiliary tree的根节点保存这个Auxiliary tree与上一棵Auxiliary tree中的哪个点相连
这个点称作他的Path parent(路上祖先)
操作:
expose(x):首先由于preferred path的定义,如果一个点被访问,
那么这个点到根节点的所有的边都会变成preferred edge,
由于每个点只有一个preferred child,所以这个点到根节点路径上的所有的点都会和原来的preferred child断开,
连接到这条新的preferred path上
假设访问x点,那么先将x点旋转到对应Auxiliary tree的根节点,
因为被访问的点是没有preferred child的,所以将Auxiliary tree中根节点(x)与右子树的边断掉,
左节点保留,将这个树的path parent旋转到对应Auxiliary tree的根节点,
断掉右子树,连接这个点与x点,相当于合并两棵Auxiliary tree,
不断地重复这一操作,直到当前X所在Auxiliary tree的path parent为null时停止,表示已经完成当前操作
find root(x):找到某一点所在树的根节点(维护森林时使用)
只需要expose(X),然后将X节点旋到对应Auxiliary tree的根节点,
然后找到这个Auxiliary tree中最左面的点
cut(x):断掉X节点和其父节点相连的边
首先expose(x),将x旋转到对应Auxiliary tree的根节点,
然后断掉Auxiliary tree中x和左节点相连的边
link(x,y):连接点x到y点上,即让x成为y的子节点
因为x为y的子节点后,
在原x的子树中,x点到根节点的所有的点的深度会被翻转过来,
所以先expose(x),然后在对应的Auxiliary tree中将x旋转到根节点,
然后将子树翻转(splay中的reverse操作),
然后expose(y),将y旋转到对应Auxiliary tree中的根节点,将x连到y就行了。
时间复杂度:
证明expose以及其他操作的时间复杂度是均摊log2N的,
具体证明参考杨哲的论文《QTREE 解法的一些研究》。
#include<cstdio>#include<cstring>#include<iostream>using namespace std;const int N=1000005;int n,m,root,cnt;int size[N],mx[N],pre[N],v[N],ch[N][2],q[N];bool rev[N];struct node{ int x,y,nxt;};node way[N<<1];int st[N],tot=0;int get(int x){ return ch[pre[x]][0]==x ? 0:1; //x是爸爸哪个孩子 }int isroot(int x){ return ch[pre[x]][0]!=x&&ch[pre[x]][1]!=x; //判断该节点是不是根 }void update(int x) //这里我维护的是子树大小 { size[x]=1; if (ch[x][0]) size[x]+=size[ch[x][0]]; if (ch[x][1]) size[x]+=size[ch[x][1]];} void push(int x) //一般是用来处理翻转操作的 { if (x&&rev[x]) { rev[x]^=1; if (ch[x][0]) rev[ch[x][0]]^=1; if (ch[x][1]) rev[ch[x][1]]^=1; swap(ch[x][0],ch[x][1]); }} void rotate(int x){ int fa=pre[x]; int grand=pre[fa]; int wh=get(x); if (!isroot(fa)) ch[grand][ch[grand][0]==fa? 0:1]=x; //爸爸不是根,那么一定有爷爷 ch[fa][wh]=ch[x][wh^1]; //爸爸和儿子的关系一定对应好 pre[ch[fa][wh]]=fa; pre[x]=grand; pre[fa]=x; ch[x][wh^1]=fa; update(fa); update(x);}void splay(int x){ int top=0; q[++top]=x; for (int i=x;!isroot(i);i=pre[i]) //把该节点到根的所有节点入队 q[++top]=pre[i]; //pre[i] while (top) push(q[top--]); //全部push for (int fa;!isroot(x);rotate(x)) if (!isroot(fa=pre[x])) rotate(get(fa)==get(x)? fa:x);}void expose(int bh){ int t=0; while (bh) { splay(bh); ch[bh][1]=t; //expose后节点的右儿子就没了 update(bh); //产生了一个新儿子,所以需要update t=bh; bh=pre[bh]; }}void makeroot(int bh){ expose(bh); splay(bh); rev[bh]^=1; //把x换到根上,那么ta以上的节点深度互换 }void link(int x,int y){ makeroot(x); pre[x]=y; //x转到根上之后链接,这样牵扯到的节点较少 }void cut(int x,int y){ makeroot(x); expose(y); splay(y); ch[y][0]=pre[x]=0; }int find(int bh){ expose(bh); splay(bh); while (ch[bh][0]) bh=ch[bh][0]; //找到根节点 return bh;}int main(){ //两点连通性 if (find(x)==find(y)) printf("Yes\n"); //两点距离 makeroot(x); expose(y); splay(y); printf("%d\n",size[ch[y][0]]); //路径上的节点权值和 makeroot(x); expose(y); splay(y); printf("%d\n",sum[y]); //节点到根的距离 int ro=find(x); makeroot(ro); expose(x); splay(x); printf("%d\n",size[ch[x][0]]); //更改节点值 makeroot(x); v[x]=z; update(x); }
- 【link-cut tree】
- Link Cut Tree (paint)
- link-cut-tree
- Link-Cut Tree
- Link/cut tree
- BZOJ2157【Link Cut Tree】
- 【笔记】Link-Cut-Tree
- (link-cut tree)
- Link-Cut-Tree总结
- Link/Cut Tree
- Link-Cut-Tree模板
- Link-Cut-Tree讲解
- link-cut tree预习
- A. Link/Cut Tree
- 【学习心得】Link-cut Tree
- Link-Cut Tree
- link-cut-tree小结
- link-cut-tree
- 阿里云部署JavaWeb初体验
- 学习这件事
- Dive into Machine Learning[沉迷机器学习,不能自拔~]
- JZOJ5165. 【NOIP2017模拟6.25】小W的动漫
- unity fixed timestep
- Link Cut Tree
- JavaScript中WebSocket介绍与运用
- ZOJ3385
- 第二章 变量和基本类型学习笔记(第一遍阅读)
- win10+ubuntu双系统(转载)
- 安装OpenJDK1.8
- Java中的Filter过滤器
- Python特性(十六):由生成器诱导的context manager
- 计蒜客 管家的忠诚(线段树水题)