树链剖分正确的入门姿势

来源:互联网 发布:音乐盒淘宝 编辑:程序博客网 时间:2024/05/20 02:29

  树链剖分并不是一个复杂的算法或者数据结构,只是能把一棵树拆成链来处理而已,换一种说法,树链剖分只是xxx数据结构/算法在树上的推广,或者说,树链剖分只是把树hash到了几段连续的区间上。(引用kuangbin大佬的)

个人对树链剖分的理解:

  因为线段树只能处理像数组一样的线性的数据,对于树这样的结构,它显得很无力,所以我们就用“树链剖分”这种东西将树拆成一小段一小段连续的链,这样我们就得到了线性的数据,但这个线性的数据使用是有限制的,比如你跨段查询或修改的时候就会出错,那怎么办呢?我们知道此时任意两个节点之间的路径都被分成了若干段,既然不能跨段,那我们就一段一段处理,然后再合并一下。这时你可能会有疑问:(没有的话,假设你有0.0)这种分段做法和暴力有多大区别?这就要看你是怎样分段的了!我们要尽量使每段更长些,那么我们就可以将任务尽可能多的交给线段树处理,(具体处理看代码),这样其实复杂度差不多是log级别的!

大致操作过程:

  通过两次dfs将整颗树分成轻链和重链(牢记同一重链在线段树上是连续的),这样做是为了将树拆成一段一段连续的小链然后放到线段上去,当查询两个节点之间的东西时,通过top[]数组和fa[]数组将这两个节点向上挪动,每次将dis[]较深的b移到fa[top[b]]处(此过程中顺便处理top[b]->b在线段树上值),重复此过程直到两个节点的top[]相同,top[]相同就意味着此时这两个节点在同一重链上,此时再重复一遍(同一重链在线段树上是连续的),所以省下可以直接就用线段树处理这两个节点之间的值了。

技巧:

  处理以节点u为根的整个子树:用线段树处理[ wei[u] , wei[u] + siz[u] - 1]即可

  查询节点u和节点v的LCA(最近公共祖先):将两点不断向上挪动并处理,直到两点的top[]相同,dis[]小的点即是最近公共祖先

  处理节点u到节点v的路径上的点:将两点不断向上挪动并处理,直到两点的top[]相同,最后用线段树处理[wei[u] , wei[v] ]即可

  处理节点u到节点v的路径上的边:将两点不断向上挪动并处理,直到两点的top[]相同,最后用线段树处理[wei[ son[u] ] , wei[v] ]即可(用每个点表示通向其父节点的边)

入门:树链剖分入门详解 (讲的很详细,不懂得细心看哦)
训练题:树链剖分从入门到精通 (难度不一,适合从入门到深入哦)

下面贴个入门参照题(参照此代码,A了入门题,就差不多算入门了)

Good Luck!(将代码注释全部打开即是POJ 2337的代码)

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<vector>using namespace std;struct Edge{    int a,b,len;}edge[10005];int tree[40005];int son[10005],fa[10005],dis[10005],top[10005],siz[10005],wei[10005],len[10005],num;vector<int>vec[10005];void dfs1(int u,int f,int d)///树链剖分套路{    fa[u]=f;    dis[u]=d;    siz[u]=1;    son[u]=0;    for(int i=0;i<vec[u].size();i++){        int v=vec[u][i];        if(v==f)continue;        dfs1(v,u,d+1);        siz[u]+=siz[v];        if(siz[son[u]]<siz[v])son[u]=v;    }}void dfs2(int u,int tp)///树链剖分套路{    top[u]=tp;    wei[u]=++num;    if(son[u])dfs2(son[u],tp);    for(int i=0;i<vec[u].size();i++){        int v=vec[u][i];        if(v==fa[u]||v==son[u])continue;        dfs2(v,v);    }}void up(int node)///线段树更新{    int ll=node<<1,rr=ll|1;    tree[node]=max(tree[ll],tree[rr]);}void maketree(int l,int r,int node)///建线段树{    if(l==r){tree[node]=len[l];return;}    int mid=(l+r)>>1;    maketree(l,mid,node<<1);    maketree(mid+1,r,node<<1|1);    up(node);}void gai(int l,int r,int node,int sign,int a)///线段树修改{    if(l>sign||r<sign)return;    if(l==r){tree[node]=a;return;}    int mid=(l+r)>>1;    gai(l,mid,node<<1,sign,a);    gai(mid+1,r,node<<1|1,sign,a);    up(node);}int query(int l,int r,int node,int ll,int rr)///线段树查询{    if(l>rr||r<ll)return -999999999;    if(l>=ll&&r<=rr)return tree[node];    int mid=(l+r)>>1,ans;    ans=query(l,mid,node<<1,ll,rr);    ans=max(ans,query(mid+1,r,node<<1|1,ll,rr));    return ans;}/*void gai2(int l,int r,int node,int ll,int rr){    if(l>rr||r<ll)return;    if(l==r){tree[node]*=-1;return;}    int mid=(l+r)>>1;    gai2(l,mid,node<<1,ll,rr);    gai2(mid+1,r,node<<1|1,ll,rr);    up(node);}*/int yongth(int a,int b)///树链剖分路径分段处理{    if(a==b)return 0;    int ta=top[a],tb=top[b],ans=-999999999;    while(ta!=tb){        if(dis[ta]>dis[tb]){            swap(ta,tb);            swap(a,b);        }        ans=max(ans,query(1,num,1,wei[tb],wei[b]));        b=fa[tb];        tb=top[b];    }    if(a==b)return ans;    if(dis[a]>dis[b])swap(a,b);    ans=max(ans,query(1,num,1,wei[son[a]],wei[b]));    return ans;}/*void Yongth(int a,int b){    int ta=top[a],tb=top[b];    while(ta!=tb){        if(dis[ta]>dis[tb]){            swap(ta,tb);            swap(a,b);        }        gai2(1,num,1,wei[tb],wei[b]);        b=fa[tb];        tb=top[b];    }    if(a==b)return;    if(dis[a]>dis[b])swap(a,b);    gai2(1,num,1,wei[son[a]],wei[b]);}*/void init(int n)///初始化{    for(int i=0;i<=n;i++){        vec[i].clear();    }}int main(){    int T;    scanf("%d",&T);    while(T--){        int n;        scanf(" %d",&n);        init(n);        int a,b;        for(int i=1;i<n;i++){///此处待优化,待读者深入时自会知晓,迫不及待的可以去看我的另一篇树链剖分题解            scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].len);            vec[edge[i].a].push_back(edge[i].b);            vec[edge[i].b].push_back(edge[i].a);        }        dfs1(1,1,1);        num=0;        dfs2(1,1);        for(int i=1;i<n;i++){///将每条边的权值按wei[]的顺序记录在len[]里面,len[]即是线段树将要处理的数组            if(dis[edge[i].a]<dis[edge[i].b])swap(edge[i].a,edge[i].b);            len[wei[edge[i].a]]=edge[i].len;        }        maketree(1,num,1);        char ch[30];        while(~scanf("%s",ch)&&ch[0]!='D'){            scanf(" %d%d",&a,&b);            if(ch[0]=='Q')printf("%d\n",yongth(a,b));            ///else if(ch[0]=='N')Yongth(a,b);            else  gai(1,num,1,wei[edge[a].a],b);        }    }    return 0;}


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孩子对学习无所谓的态度怎么办 孩子不上进什么都无所谓怎么办 倔强的学生不理老师怎么办 一岁宝宝特别犟怎么办 孩子遇到问题喜欢发脾气怎么办 企业培养新人跑了怎么办 二年级学生读不懂题目怎么办 6岁宝宝有鼻炎怎么办 外地儿童怎么办北京医保卡 江苏联宝投资的钱怎么办 银行叫开了证券怎么办 中班心理健康我不开心了怎么办 大学生心理健康课总是抢不到怎么办 孩子成绩提不上去怎么办 孩子数学不开窍怎么办 二年级 初中孩子语文不开窍怎么办 分到的班级都是差生怎么办 初三了英语差怎么办呀 初三了英语差的很怎么办 五年级英语太差怎么办 听课效率没有自学效率高怎么办 小学生不好好写作业怎么办 带的家教成绩没有提高怎么办 学生出国学校成绩证明怎么办 学生成绩考差了班主任怎么办 高一的学生成绩跟不上怎么办 综合素质评价手册丢了怎么办 小学综合素质评价手册丢了怎么办 人体质不出汗差怎么办 儿子一年级语文成绩太差怎么办 小孩读一年级差几个月怎么办 小孩一年级下学期数学差怎么办 小学五年成绩差怎么办 生完孩子记忆力不好怎么办 生了孩子之后记忆力不好怎么办 孩子记忆力差学习不好怎么办 党课结业证丢了怎么办 对三年级不听话的学生应该怎么办 素质报告单丢了怎么办 社保小红本丢了怎么办 小红本丢了怎么办