以下是转的别人的
(后有模板)
这几天学习了一下树链剖分,顺便写一下我的理解、
早上看了一下别人的讲解,云里雾里,终于算是搞懂了、
树链剖分是解决在树上进行插点问线,插线问点等一系列树上的问题
假如现在给你一棵树,然后没两条边之间有一条权值,有一些操作,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*/