• 树链剖分边权模板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;
    }
  • 相关阅读:
    我所理解的NAT
    H3C防火墙安全策略故障排查思路
    职场建议
    小型企业典型网络内部拓扑
    echo&printf
    笔挺站立 昂首挺胸
    Jordan Peterson 的建议
    刚才思考的两个问题
    高手的见解
    一个企业网络管理人员的注意事项
  • 原文地址:https://www.cnblogs.com/zsben991126/p/10039282.html
Copyright © 2020-2023  润新知