树链剖分

以下是转的别人的


(后有模板)

这几天学习了一下树链剖分,顺便写一下我的理解、

早上看了一下别人的讲解,云里雾里,终于算是搞懂了、


树链剖分是解决在树上进行插点问线,插线问点等一系列树上的问题

假如现在给你一棵树,然后没两条边之间有一条权值,有一些操作,1:x---y之间的最大权值是多少,2:改变x---y之间的权值

当前这样的操作有很多,如果直接用暴力的方法的话肯定不行,那么就要想一个好的方法,我们可以想一下能不能借助线段树解决,能不能想一种方法对树上的边进行编号,然后就变成区间了。那么我们就可以在线段树上进行操作了,树链剖分就是这样的一个算法


当然编号不是简单的随便编号,如果我们进行随便的编号,然后建立一个线段树,如果要更新一个边的权值,是log2(n)的复杂度,而查找的话,我们要枚举x--y的之间的所有的边,假如我们随便以一个点为根节点进行编号,最大的长度是树的直径,这个值本身是比较大的,而在线段树上查找任意一个区间的复杂度也是log2(n),这样查找一次的时间复杂度比直接暴力还要高,所以很明显是不行的。

那么就要想想办法了,我们能不能把x--y之间的一些边一块儿查找,这就是关于树链剖分的重边和轻边,

重边:某个节点x到孩子节点形成的子树中节点数最多的点child之间的边,由定义发现除了叶子节点其他节点只有一条重边

重边是可以放在一块儿更新的,而有

性质:从根到某一点的路径上轻边、重边的个数都不大于logn。

所以这样查找的时间复杂度相当于log2(n)


其实树链剖分就是把边哈希到线段树上的数据结构

实现的话很简单,用两个dfs处理数数的信息,重边以及轻边,然后就是一些线段树的操作了。

模板“:以spoj 375 为例

//为易错点

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#define inf 1<<30
using namespace std;
const int maxn = 50010;
int size[maxn],son[maxn],dep[maxn],fa[maxn];
int top[maxn],tid[maxn],tim,num[maxn];
int ne[maxn<<1],be[maxn<<1],to[maxn<<1],w[maxn<<1],e;
bool p[maxn];
int n;
struct T{
	int u,v,w;
}tmp[maxn];
void add(int x,int y,int z){
	to[++e]=y;
	ne[e]=be[x];
	be[x]=e;
	w[e]=z;
}
void dfs1(int x,int f,int d){
	size[x]=1;
	dep[x]=d;
	fa[x]=f;
	p[x]=1;
	for(int i=be[x];i;i=ne[i]){
		int v=to[i];
		if(!p[v]){
			dfs1(v,x,d+1);
			size[x]=size[v]+size[x];//
			if(!son[x]||size[v]>size[son[x]])son[x]=v; //
		}
	}
}
void dfs2(int x,int tp){
	top[x]=tp;
	tid[x]=++tim;
	p[x]=1;
	if(!son[x])return ;
	dfs2(son[x],tp);
	for(int i=be[x];i;i=ne[i]){
		int v=to[i];
		if(!p[v]){
			dfs2(v,v);
		}
	}
}
int tree[maxn];
void build(int h,int l,int r){
	if(l==r){
		tree[h]=num[l];
		return ;
	}
	int mid=l+r>>1;
	build(h<<1,l,mid);
	build(h<<1|1,mid+1,r);
	tree[h]=max(tree[h<<1],tree[h<<1|1]);
}
void updata(int h,int l,int r,int p,int w){
	if(l==r){
		tree[h]=w;
		return ;
	}
	int mid=l+r>>1;
	if(p>mid)updata(h<<1|1,mid+1,r,p,w);
	else updata(h<<1,l,mid,p,w);
	tree[h]=max(tree[h<<1],tree[h<<1|1]);
}
int Query(int h,int l,int r,int q,int w){
	if(l==q&&r==w)return tree[h];
	int mid=l+r>>1;
	if(q>mid)return Query(h<<1|1,mid+1,r,q,w);
	else if(w<=mid)return Query(h<<1,l,mid,q,w);
	else{
		return max(Query(h<<1,l,mid,q,mid),Query(h<<1|1,mid+1,r,mid+1,w));// 
	}
}
void change(int x,int val){
	if(dep[tmp[x].u]>dep[tmp[x].v]){
		updata(1,2,n,tid[tmp[x].u],val);
	}
	else updata(1,2,n,tid[tmp[x].v],val);
}
int query(int x,int y){
	int ans=-inf;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		ans=max(ans,Query(1,2,n,tid[top[x]],tid[x]));
		x=fa[top[x]];
	}
	if(dep[x]>dep[y])swap(x,y);
	if(x!=y)return max(ans,Query(1,2,n,tid[x]+1,tid[y]));
	return ans;
}
int main(){
	int i,j,k,m;
	int t;
	scanf("%d",&t);
	char s[20];
	while(t--){
		memset(be,0,sizeof(be));
		memset(son,0,sizeof(son));
		memset(p,0,sizeof(p));
		e=0;
		tim = 0;
		scanf("%d",&n);
		for(i=1;i<=n-1;i++){
			int x,y,z;
			scanf("%d%d%d",&x,&y,&z);
			add(x,y,z);
			add(y,x,z);
			tmp[i].u=x,tmp[i].v=y,tmp[i].w=z;
		}
		dfs1(1,1,1);
		memset(p,0,sizeof(p));
		dfs2(1,1);
		for(i=1;i<=n-1;i++){
			if(dep[tmp[i].u]<dep[tmp[i].v])num[tid[tmp[i].v]]=tmp[i].w;//
			else num[tid[tmp[i].u]]=tmp[i].w;
		}
		build(1,2,n);
		while(1){
			scanf("%s",s);
			int x,y;
			if(s[0]=='D')break;//
			scanf("%d%d",&x,&y);
			if(s[0]=='Q'){
				printf("%d\n",query(x,y));
			}
			else{
				change(x,y);
			}
		}
	}
	return 0;
}
/*1

3
1 2 1
2 3 2
QUERY 1 2
CHANGE 1 3
QUERY 1 2
Q 1 3
Q 2 3
DONE*/


posted @ 2017-01-15 16:29  Drinkwater_cnyali  阅读(104)  评论(0编辑  收藏  举报