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