动态树 bzoj2049

32 篇文章 0 订阅
31 篇文章 0 订阅

经过一个下午的折腾,经过四个小时的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 10010

using 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);
		}
		else
		if(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");
			else
				printf("No\n");
		}
	}
	return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值