树链剖分

来源:互联网 发布:优酷网络连接失败 编辑:程序博客网 时间:2024/06/05 21:49

以下是转的别人的


(后有模板)

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

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


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

假如现在给你一棵树,然后没两条边之间有一条权值,有一些操作,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<<30using 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;}/*131 2 12 3 2QUERY 1 2CHANGE 1 3QUERY 1 2Q 1 3Q 2 3DONE*/


0 0