树链剖分边权模板spoj375

树链剖分是树分解成多条链来解决树上两点之间的路径上的问题

如何求出树链:第一次dfs求出树上每个结点的大小和深度和最大的儿子,第二次dfs就能将最大的儿子串起来并hash(映射)到线段树上(或者其他数据结构上),这就是一条重链。

一些性质:1.在树链上进行的算法要额外乘以一个logn:因为找u,v的lca复杂度为O(logn),在找lca的过程中进行其它算法操作,所以算总复杂度时要额外乘上logn

     2.如果(v,u)为轻边,则siz[u] * 2 < siz[v]

       3.从根到某一点的路径上轻链、重链的个数都不大于logn

     4.可以沿着重链求出lca  每次求复杂度O(logn)

     5.如果出现边权,那就将边权映射到对应的树节点上,根节点是没有被映射的

/*
树链剖分:在一棵树上进行路径的修改,求极值,求和 
树链:树上的路径
剖分:把路径分为重链和轻链
siz[v]表示已v为根的子树的节点数,dep[v]表示v的深度(根深度为1)
top[v]表示v所在的链的顶端结点,fa[v]表示v的父亲,
son[v]表示与v在同一重链上的v的儿子节点(姑且称为重儿子)
w[v]表示v与其父亲节点的连边(姑且称为v的父边)在线段树中的位置 

重儿子:siz[u]为v的子节点中siz值最大的,那么u就是v的重儿子
轻儿子:v的其它子节点
重边:点v与其重儿子的连边
轻便:点v与其轻儿子的连边
重链:由重边构成的路径
轻链:轻边 

性质1:(v,u)为轻边,则siz[u]*2<siz[v]
性质2:从根到某一点的路径上轻链,重链的个数都不大于logn

算法:两个dfs求fa,dep,siz,son,top,w
    dfs_1:把fa,dep,siz,son求出来
    dfs_2:对于v,son[v]存在时(v不是叶子节点),显然有top[son[v]]==top[v]
        v的重边应当在v的父边的后面,记w[son[v]]=totw+1 ,totw表示最后加入的一条边在线段树中的位置
         此时,为了使一条重链各边在线段树中连续分布,应当进行dfs_2(son[v]);
        v的各个轻儿子u,显然有top[u]=u,并且w[u]=totw+1,进行dfs_2过程,这就求出了top和w 
        
修改操作:将u到v的路径长得每条边的权值都加上某值x
    记 f1=top[u],f2=top[v]
    当 f1 != f2 :设dep[f1]>=dep[f2],更新u到f1的父边的权值(logn),并使u=fa[f1]
    当 f1 == f2 : u,v在同一条重链上,若u与v不是同一点,就更新u到v路径上的边的权值
                否则修改完成
    重复上述步骤 
     
spoj375 
*/
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define MAXN 10010
using namespace std;
struct Edge{
    int to, next;
}edge[MAXN*2];
int head[MAXN], tot;
int top[MAXN];//所在的重链的顶端结点
int fa[MAXN];//父亲
int deep[MAXN];//深度
int num[MAXN];//子节点数
int p[MAXN];//p[v]表示v与其父亲节点的连边在线段树中的位置
int fp[MAXN];//和p数组相反
int son[MAXN];//重儿子
int pos;

void init(){
    tot = 0;
    memset(head, -1, sizeof(head));
    pos = 0;
    memset(son, -1, sizeof(son));
}
//链式前向星 
void addedge(int u, int v){
    edge[tot].to = v;
    edge[tot].next = head[u];//下一条边存储的下标 
    head[u] = tot++;
}
//第一遍求出fa,deep,num,son 
void dfs1(int u, int pre, int d){
    deep[u] = d;
    fa[u] = pre;
    num[u] = 1;
    //遍历u的每个子节点 
    for(int i = head[u];i != -1; i = edge[i].next){
        int v = edge[i].to;
        if (v != pre){
            dfs1(v, u, d+1);
            num[u]+=num[v];
            if (son[u]==-1||num[v]>num[son[u]])
                son[u] = v; 
        }
    } 
} 
//第二次dfs求出top和p
void getpos(int u, int sp){
    top[u] = sp;
    if (son[u] != -1){//如果不是叶子节点,必有重儿子 
        p[u] = pos++; //表示u与父亲结点的连边在线段树上的位置 
        fp[p[u]] = u;
        getpos(son[u], sp);//顺着重链dfs 
    }
    else {//叶子节点就不要dfs了 
        p[u] = pos++;
        fp[p[u]] = u;
        return;
    }
    for(int i = head[u]; i != -1; i = edge[i].next){//对于各个轻儿子 
        int v = edge[i].to;
        if (v != son[u] && v != fa[u])
            getpos(v, v);
    }
} 

//线段树
struct Node{
    int l, r;
    int Max;
}segTree[MAXN*3];
void build(int i, int l, int r){
    segTree[i].l = l;
    segTree[i].r = r;
    segTree[i].Max = 0;
    if (l==r)
        return;
    int mid = l+r >> 1;
    build(i<<1, l, mid);
    build(i<<1|1, mid+1, r); 
} 
void push_up(int i){
    segTree[i].Max = max(segTree[i<<1].Max, segTree[i<<1|1].Max);
}
void update(int i, int k, int val){//更新线段树的第k个值为val 
    if(segTree[i].l == k && segTree[i].r == k){
        segTree[i].Max = val;
        return;
    } 
    int mid = segTree[i].l+segTree[i].r >> 1;
    if (k <= mid)
        update(i<<1, k, val);
    else
        update(i<<1|1, k, val);
    push_up(i);
}
//查询线段树中[l,r]的最大值,一条重链上的最大权值是很好查询的 
int query(int i, int l, int r){
    if(segTree[i].l==l&&segTree[i].r==r)
        return segTree[i].Max;
    int mid = segTree[i].l+segTree[i].r>>1;
    if (r<=mid)
        return query(i<<1, l, r);
    else if (l > mid)    
        return query(i<<1|1, l, r);
    else 
        return max(query(i<<1, l, mid), query(i<<1|1, mid+1, r)); 
}
//查询u->v边的最大值
int find(int u, int v){
    int f1 = top[u], f2 = top[v];
    int tmp = 0;
    while(f1 != f2){//两个点不是在同一重链上 
        if (deep[f1]<deep[f2]){
            swap(f1, f2);
            swap(u, v);
        }//默认deep[f1]比较大
        //当 f1 != f2 :设dep[f1]>=dep[f2],更新u到f1的父边的权值(logn),并使u=fa[f1]
        tmp = max(tmp, query(1, p[f1], p[u]));
        u = fa[f1];
        f1 = top[u]; 
    }//最后一定会在同一条重链上 
    if (u==v)
        return tmp;
    if (deep[u]>deep[v])
        swap(u, v);
    return max(tmp, query(1, p[son[u]], p[v]));
} 
int e[MAXN][3];
 
int main(){
    int T;
    int n;
    scanf("%d", &T);
    while(T--){
        init();
        scanf("%d", &n);
        for(int i = 0; i < n-1; i++){
            scanf("%d%d%d", &e[i][0], &e[i][1], &e[i][2]);
            addedge(e[i][0], e[i][1]);
            addedge(e[i][1], e[i][0]);
        }
        dfs1(1,0,0);
        getpos(1,1);
        build(1,0,pos-1);
        for(int i = 0; i < n-1; i++){
            if (deep[e[i][0]]>deep[e[i][1]])
                swap(e[i][0], e[i][1]);
            update(1, p[e[i][1]], e[i][2]);//把(u,v)的权值加入线段树 
        } 
        char op[10];
        int u, v;
        while(scanf("%s", op)==1){
            if (op[0]=='D')
                break;
            scanf("%d%d", &u, &v);
            if (op[0]=='Q')
                cout << find(u, v)<<endl;
            else 
                update(1, p[e[u-1][1]], v);//修改第u条边,其下标就是u-1 
        }
    }
    return 0;
} 

 简化后的代码

 /*
树链剖分:
将每一条重链在线段树上铺开,访问顺序越靠前的链越靠线段树左边
*/
#include<bits/stdc++.h>
using namespace std;
#define maxn 10010
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
struct Edge{
    int to,next;
}edge[maxn<<1];
int head[maxn],tot;
int top[maxn],fa[maxn],deep[maxn],num[maxn],p[maxn],fp[maxn],son[maxn],pos;
int init(){
    tot=pos=0;
    memset(head,-1,sizeof head);
    memset(son,-1,sizeof son);
}
void addedge(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void dfs1(int u,int pre,int d){
    deep[u]=d;fa[u]=pre;num[u]=1;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(v==pre) continue;
        dfs1(v,u,d+1);
        num[u]+=num[v];
        if(son[u]==-1 || num[son[u]]<num[v]) son[u]=v;
    }
}
void getpos(int u,int sp){
    top[u]=sp;p[u]=pos++;fp[p[u]]=u;
    if(son[u]==-1) return;
    else getpos(son[u],sp);
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(v!=son[u] && v!=fa[u]) getpos(v,v);//重新开一条重链
    }
}
int Max[maxn<<2];
inline void pushup(int rt){Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);}
void build(int l,int r,int rt){
    Max[rt]=0;
    if(l==r) return;
    int m=l+r>>1;
    build(lson);
    build(rson);
}
void update(int pos,int val,int l,int r,int rt){
    if(l==r) {Max[rt]=val;return;}
    int m=l+r>>1;
    if(pos<=m) update(pos,val,lson);
    else update(pos,val,rson);
    pushup(rt);
}
int query(int L,int R,int l,int r,int rt){
    if(L<=l && R>=r) return Max[rt];
    int m=l+r>>1,res=-1;
    if(L<=m) res=max(res,query(L,R,lson));
    if(R>m) res=max(res,query(L,R,rson));
    return res;
}
int find(int u,int v){//查询u->v边上的最大值
    int f1=top[u],f2=top[v],tmp=0;
    while(f1!=f2){
        if(deep[f1]<deep[f2]){swap(f1,f2);swap(u,v);}
        tmp=max(tmp,query(p[f1],p[u],0,pos-1,1));
        u=fa[f1];f1=top[u];
    }
    if(u==v) return tmp;
    if(deep[u]>deep[v]) swap(u,v);
    return max(tmp,query(p[son[u]],p[v],0,pos-1,1));
}
int e[maxn<<1][3];//保存边
int main(){
    int T,n;
    cin >> T;
    while(T--){
        init();
        scanf("%d",&n);
        for(int i=0;i<n-1;i++){
            scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]);
            addedge(e[i][0],e[i][1]);addedge(e[i][1],e[i][0]);
        }
        dfs1(1,0,0);getpos(1,1);build(0,pos-1,1);
        for(int i=0;i<n-1;i++){
            if(deep[e[i][0]]>deep[e[i][1]]) swap(e[i][0],e[i][1]);
            update(p[e[i][1]],e[i][2],0,pos-1,1);//其实是把边权映射到点上,第一条边在线段树上对应的下标是1,根节点对应的虚边在线段树上下标是0,权值也是0
        }
        char op[10];
        int u,v;
        while(scanf("%s",op)==1){
            if(op[0]=='D') break;
            scanf("%d%d",&u,&v);
            if(op[0]=='Q') printf("%d
",find(u,v));
            else update(p[e[u-1][1]],v,0,pos-1,1);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/zsben991126/p/10039282.html