动态树 bzoj2049

来源:互联网 发布:计算机专业除了编程 编辑:程序博客网 时间:2024/05/16 04:11

经过一个下午的折腾,经过四个小时的debug,我的LCT基本定型了。。。

为了加深理解大概写点。。。


本文地址:http://blog.csdn.net/u012457935/article/details/19015059


其实动态树是一类问题啊,这个问题包含了很多的分治,具体可以去fhq666看,不过我们大多树情况还是把LCT(link cut tree)称为动态树

好吧不废话了。。。


动态树:

是一种基于树上操作的数据结构,程序大概只有几k但是却可以实现许多操作。(写前最好有树链剖分的基础)

实际上这种数据结构与树链剖分有异曲同工之处,都是化树为链,只不过树链剖分是在线段树上实现的,而动态树是在splay上的实现的。

当然,可以理解为动态树是熟料剖分的拓展,从功能上可以看出来。。

还有一个重点就是动态树所维护的树是可以动的(废话。。。。)这就导致树链剖分中的轻重边定义失效了,所以动态树上的轻重边的定义是基于操作的,不过这并不重要,原来在树链剖分中的各种定理也是可以用的。。要是不能用...........你就别用了。

这大概就是动态树的基础知识。。。个人认为已经比较详细了。。


动态树的基本操作:

首先是最基础的access操作:

即将当前节点到root上的所有边都变为重边

其他是一些基本的操作:

build_tree

很简单啊就是每个点一棵树(请自行区分本文中的树是指splay还是题中的树)

evert

将当前节点x到当前根节点的所有树边都反向,经过这样的一次操作以后根就是当前节点了。。

cut 

将节点x与其树中的边删去

link

使得节点y成为x的孩子

find

找到当前节点x在树中的根节点。


下面将一些具体实现的东西:

①access(x)

首先如果当前节点x与root在一棵树中,那么直接解决问题了就

否则设u为x所在树的path_parent,那么将u通过splay操作转到根,然后用x所在的树代替u的右子树。同时将原来u的右子树w的path_parent设为x

②find(x)

首先对当前节点access(我说是基础吧。。。)然后你懂得

③evert(x)

这个操作的正经定义应该是是节点x成为当前树的根

不过这等价于x到root的边都取反

首先还是access,然后在平衡树上取反嘛。。。。正常操作是o(n)的。。。。

不过我们可以借鉴线段树上的标记操作,同样给节点打上res操作,这样就可以了!不过一定要及时正确的使用down函数进行标记的下放,要不然多放了会tle,少放了会wa

④link(x,y)

为了保证合并后的树是合法的,我们首先要使x成为其所在树的根节点,同时将x splay到根,此时x的左子树是空的(废话。。。。)我们此时就可以把y也splay到他所在树的根然后连到x的左子树上。

好机智!

⑤cut(x,y)

首先将x置为根,在对y进行access,那么这个时候xy就在一棵平衡树中了,这个时候我们只需要分离x和他的左子树目的就达成了。。。


这些就是基本的操作了。。如果有什么更神的操作再说。。。

---------------------------------------------------------by-----------------wbysr-------------------------------------------------------------------------------------------------------------------------------------------------

下面是例题bzoj2049的代码,这是个模板题,建议自己写写。

#include<iostream>#include<cstring>#include<cstdio>#include<string>#include<algorithm>#define MAX 10010using namespace std;int n,m;struct anode{int id,father,path_parent,res;int child[2];};struct LinkCutTree{anode node[MAX];int cnt,location[MAX];void build(int x){int p=++cnt;location[x]=p;node[p].father=node[p].child[0]=node[p].child[1]=node[p].res=0;node[p].id=x;}void down(int x){if(!node[x].res)return;node[x].res=0;node[node[x].child[0]].res^=1;node[node[x].child[1]].res^=1;swap(node[x].child[0],node[x].child[1]);}void rotate(int p){int q=node[p].father,y=node[q].father,x=(node[q].child[1]==p);down(q);down(p);node[node[q].child[x]=node[p].child[x^1]].father=q;node[node[p].child[x^1]=q].father=p;node[p].father=y;node[p].path_parent=node[q].path_parent;node[q].path_parent=0;if(y)node[y].child[node[y].child[1]==q]=p;return;}void splay(int x,int aim=0){while(node[x].father){int fa=node[x].father;down(fa);if(node[fa].child[0])down(node[fa].child[0]);if(node[fa].child[1])down(node[fa].child[1]);rotate(x);}}void access(int x){splay(x);down(x);int y=node[x].child[1];node[x].child[1]=node[y].father=0;node[y].path_parent=x;for(y=node[x].path_parent;y;y=node[x].path_parent){splay(y);down(y);int r=node[y].child[1];node[r].father=0;node[r].path_parent=y;node[y].child[1]=x;node[x].father=y;node[x].path_parent=0;x=y;}splay(x);}void cut(int x,int y){evert(x);access(y);splay(y);down(y);node[node[y].child[0]].father=0;node[y].child[0]=0;}int find(int x){access(x);splay(x);down(x);while(node[x].child[0])x=node[x].child[0];//printf("xxx %d\n",x);splay(x);return x;}int get_root(int x){return node[find(x)].id;}void evert(int x)//使x成为所在树的根节点{access(x);splay(x);node[x].res^=1;down(x);}void link(int x,int y)//使x成为y的子节点{evert(x);splay(x);down(x);access(y);splay(y);down(y);node[x].child[0]=y;node[y].father=x;}void add(int x,int y){link(location[x],location[y]);}void destory(int x,int y){cut(location[x],location[y]);}}wbysr;int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)wbysr.build(i);while(m--){char ss[300];int u,v;scanf("%s",ss);scanf("%d%d",&u,&v);if(ss[0]=='C'){wbysr.add(u,v);}elseif(ss[0]=='D')wbysr.destory(u,v);else{int x=wbysr.get_root(u);int y=wbysr.get_root(v);if(x==y)printf("Yes\n");elseprintf("No\n");}}return 0;}

2 0
原创粉丝点击