【数据结构】树链剖分总结

来源:互联网 发布:马东的软件 编辑:程序博客网 时间:2024/06/06 00:14

树链剖分是一种基于树的算法,将树转化为线性结构(如线段树),可以说是一种应用性十分强的算法,并且在近年来的考题中也常常出现。但其实思维上并不是一个很困难的东西,实现起来也非常好写。

剖分技巧通常有三种:

1,随机剖分
2,盲目剖分
3,启发式剖分

在随机数据的情况下,三者的实际速度并没有太大差别,但如果数据有一定特征,那么前两个方法就会逊色不少。所以启发式剖分就是我们最常用的树链剖分方式。

启发式剖分:按照大小,将每个点连向最大子树的边成为重边,其他边称为轻边。由重边连接形成的链就称为重链。

启发式剖分有一个很重要的性质:
从根节点到任意一个节点的路径上最多经过logn条重链,logn条轻边
证明很容易想,这里就简单提及一下:

因为轻边所连接的子树大小不大于总子树大小的一半,所以每走一次轻边,能到达的点就会减少至少一半,因此最多只会经过logn条轻边。因为最多只走logn条轻边,所以经过的重链数量也必然小于logn

实现:

对于树链剖分来说,实现技巧是比较关键的内容。
如果自己写,很容易写成200多行的大模板,然而有良好的实现技巧用不到100行就可以解决。

剖分:
树的剖分用两次dfs完成,
第一次:寻找重边,统计子树大小,父亲节点,构造深度信息。

void dfs1(int x,int fax,int deep){    dep[x]=deep;    fa[x]=fax;    son[x]=-1;    sz[x]=1;    for(int i=0;i<a[x].size();i++){        if(a[x][i]==fax)            continue;        dfs1(a[x][i],x,deep+1);        sz[x]+=sz[a[x][i]];        if(son[x]==-1||sz[son[x]]<sz[a[x][i]])            son[x]=a[x][i];    }}

第二次:构造重链,按照DFS序将一条重链上的点置于数据结构上的相邻位置,标记重链顶端节点,构造DFS序的信息。

void dfs2(int x,int tp){    top[x]=tp;    tid[x]=++dcnt;    rnk[dcnt]=x;    if(son[x]==-1) return;    dfs2(son[x],tp);    for(int i=0;i<a[x].size();i++)        if(a[x][i]!=son[x]&&a[x][i]!=fa[x])            dfs2(a[x][i],a[x][i]);}

访问:
通常的访问会给出一个点对(u,v),询问路径上的信息。
处理方式和LCA有些许类似,详情可参照代码:

int Query(int x,int y){//求x,y两点路径上的边权和    int res=0;    while(top[x]!=top[y]){        if(dep[top[x]]<dep[top[y]])            swap(x,y);        res+=Query1(1,n,1,tid[top[x]],tid[x]);        x=fa[top[x]];    }    if(dep[x]>dep[y])        swap(x,y);    res+=Query1(1,n,1,tid[x],tid[y]);    return res;}

例题:Qtree
代码:

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<vector>#define SF scanf#define PF printf#define MAXN 50010using namespace std;vector<int> a[MAXN],id[MAXN];int tree[MAXN*4],dcnt,n,t;int sz[MAXN],dep[MAXN],top[MAXN],son[MAXN],rnk[MAXN],tid[MAXN],fa[MAXN];int e2v[MAXN],v2e[MAXN],len[MAXN];void build(int l,int r,int id){    if(l==r){        tree[id]=len[v2e[rnk[l]]];        return ;    }    int mid=(l+r)>>1;    build(l,mid,id*2);    build(mid+1,r,id*2+1);    tree[id]=max(tree[id*2],tree[id*2+1]);}void Change(int l,int r,int id,int del,int val){    if(l==r){        tree[id]=val;        return ;    }    int mid=(l+r)>>1;    if(del<=mid)        Change(l,mid,id*2,del,val);    else        Change(mid+1,r,id*2+1,del,val);    tree[id]=max(tree[id*2],tree[id*2+1]);}int Query(int l,int r,int id,int l1,int r1){    if(l1<=l&&r<=r1){        return tree[id];    }    int mid=(l+r)>>1;    if(mid>=r1)        return Query(l,mid,id*2,l1,r1);    if(mid<l1)        return Query(mid+1,r,id*2+1,l1,r1);    return max(Query(l,mid,id*2,l1,r1),Query(mid+1,r,id*2+1,l1,r1));}void dfs1(int x,int fax,int deep){    fa[x]=fax;    dep[x]=deep;    sz[x]=1;    son[x]=-1;    for(int i=0;i<a[x].size();i++){        int v=a[x][i];        if(v==fax)            continue;        e2v[id[x][i]]=v;        v2e[v]=id[x][i];        dfs1(v,x,deep+1);        sz[x]+=sz[v];        if(son[x]==-1||sz[v]>sz[son[x]])            son[x]=v;    }}void dfs2(int x,int tp){    top[x]=tp;    tid[x]=++dcnt;    rnk[dcnt]=x;    if(son[x]==-1)        return ;    dfs2(son[x],tp);    for(int i=0;i<a[x].size();i++)        if(a[x][i]!=son[x]&&a[x][i]!=fa[x])            dfs2(a[x][i],a[x][i]);}int Que(int x,int y){    int res=0;    while(top[x]!=top[y]){        if(dep[top[x]]<dep[top[y]])            swap(x,y);        res=max(res,Query(1,n,1,tid[top[x]],tid[x]));        x=fa[top[x]];    }    if(dep[x]>dep[y])        swap(x,y);    if(x!=y)        res=max(res,Query(1,n,1,tid[x]+1,tid[y]));    return res;}void init(){    dcnt=0;    memset(tree,0,sizeof tree);    memset(a,0,sizeof a);    memset(id,0,sizeof id);}char s[10];int main(){    //freopen("data.in","r",stdin);    //freopen("data.out","w",stdout);    SF("%d",&t);    int u,v,val;    while(t--){        SF("%d",&n);        init();        for(int i=1;i<n;i++){            SF("%d%d%d",&u,&v,&val);            a[u].push_back(v);            a[v].push_back(u);            id[u].push_back(i);            id[v].push_back(i);            len[i]=val;        }        dfs1(1,0,1);        dfs2(1,1);        build(1,n,1);        SF("%s",s);        while(s[0]!='D'){            SF("%d%d",&u,&v);            if(s[0]=='C'){                Change(1,n,1,tid[e2v[u]],v);            }            else{                PF("%d\n",Que(u,v));            }            SF("%s",s);        }    }}
原创粉丝点击