LCT学习笔记

来源:互联网 发布:centos 7 3306端口 编辑:程序博客网 时间:2024/06/05 06:03

动态树问题:
动态加边,删边,始终保持是一棵或多棵树。
支持换根,修改点、边、路径权值(等信息),查询路径权值等。

思想:把树分成若干个部分维护。和树链剖分相似,树链剖分的一些题目也可以用LCT来做。LCT的均摊复杂度是log²n,杨哲的论文(百度搜一下,%大神)有证明,但我太弱,看不懂,也不想研究……

一些定义:
access(x) :访问x,这个操作是LCT关键;
Preferred child(更喜欢的儿子~?):一个点P,设P点子树中最近被访问的节点为x,那么x为P点的Prefferred child,如果最后访问P本身,那么P没有Prefferred child。x与P之间的边为Preferred edge,由Preferred edge组成的不可延长的路径叫做Preferred path,与树链剖分的重链差不多。
这样就有一些性质,所有点都在且仅在一个Prefferred path中。所有的Prefferred path包含了这棵树所有点。
画图丑
access(10);access(13);access(7);access(4);树就变成了这样(图片丑)。

这样的每棵splay我们称为Auxiliary tree(辅助树),每个Auxiliary tree的根节点保存这个Auxiliary tree与上一棵Auxiliary tree中的哪个点相连。这个点称作他的Path parent。
像上图中5的Path parent为2;Path parent相当于儿子认爹,爹不认儿子;由2号点我无法找到5号点。

树链剖分的树是静态的,所以用线段树.树状数组搞,但这是动态树,所以对于每一条Prefferred path,以距离根的距离为关键字,用Splay(Treap应该也可以)维护,深度小的在左儿子,深度大的在右儿子。

主要操作:
access(X):首先由于preferred path的定义,如果一个点被访问,那么这个点到根节点的所有的边都会变成preferred edge,由于每个点只有一个preferred child,所以这个点到根节点路径上的所有的点都会和原来的preferred child断开,连接到这条新的preferred path上。假设访问X点(6号点),那么先将X点旋转到对应Auxiliary tree的根节点,然后因为被访问的点是没有preferred child的,所以将Auxiliary tree中根节点(X)与右子树(10)的边断掉,左节点(3)保留,将这个树的path parent(1)旋转到对应Auxiliary tree的根节点,断掉右子树(2,4),连接这个点(1)与X(6)点,相当于合并两棵Auxiliary tree,不断地重复这一操作,直到当前X所在Auxiliary tree的path parent为null时停止,表示已经完成当前操作。

inline void access(int x){    for(int t=0;x;t=x,x=fa[x])        splay(x),ch[x][1]=t,up(x);}

这样就有一些神奇的事情。
access(x);splay(x);
这时候X为他所在Auxiliary tree的根节点,并且没有右子树;
既然这样,我把这棵树所有左右子树都翻转过来,不就表示以x为根的树了吗.

inline void make_root(int x){access(x);splay(x);rev[x]^=1;}

打了标记之后,每一次Splay都要把这个点以及上面所有祖先标记释放。

stack<int> S;inline void splay(int x){    S.push(x);    for(int i=x;!isroot(i);i=fa[i])        S.push(fa[i]);    while(!S.empty())    {        int v=S.top();        S.pop();pushdown(v);    }    ………………}
inline void pushdown(int x){    if(rev[x])    {        rev[ch[x][0]]^=1;        rev[ch[x][1]]^=1;        swap(ch[x][0],ch[x][1]);        rev[x]=0;    }}

link(x,y) x,y之间建边。

inline void link(int u,int v){make_root(u);fa[u]=v;}

cut(x,y)删除x,y之间的边。
先把x作为根,然后access(y);找到x,y之间路径(一条边),splay(y);由于x为根,深度最小,所以x在y的左儿子;

inline void cut(int x,int y){    make_root(x);access(y);splay(y);    ch[y][0]=fa[x]=0,up(y);}

find(x);找x的根;

inline int find(int x){    access(x);splay(x);    while(ch[x][0]) x=ch[x][0];    return x;}

我去终于写完了。
感谢dqs学长的博客讲解。以及学习过程中抄的fqk,yzy大神的模板。
感谢各种资料……
http://blog.csdn.net/loi_dqs
http://blog.csdn.net/Phenix_2015
http://blog.csdn.net/ws_yzy
话说我不会博客加友链啊。
还有我很弱,这东西才学了一天,大家不要被我误导。

5 0