zoukankan      html  css  js  c++  java
  • HDU 3966 Aragorn's Story(模板题)【树链剖分】+【线段树】

    <题目链接>

    题目大意:

    给定一颗带点权的树,进行两种操作,一是给定树上一段路径,对其上每个点的点权增加或者减少一个数,二是对某个编号点的点权进行查询。

    解题分析:

    树链剖分的模板题,还不会树链剖分可以看这里 >>>

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define Lson l,mid,rt<<1
    #define Rson mid+1,r,rt<<1|1
    using namespace std;
    const int M = 5e4+7;
    int head[M],sz[M],son[M],f[M],tid[M],rnk[M],dep[M],top[M];
    int tot,cnt,n,m,q,a[M],pos,ans;
    //sz[]数组,用来保存以x为根的子树节点个数
    //top[]数组,用来保存当前节点的所在链的链首
    //son[]数组,用来保存重儿子
    //dep[]数组,用来保存当前节点的深度
    //f[]数组,用来保存当前节点的父亲
    //tid[]数组,用来保存树中每个节点剖分后的新编号
    //rnk[]数组,线段树中编号对应的原节点编号
    struct edge{
        int v,next;
    }e[M<<1];
    struct tree
    {
        int sum,lazy,l,r;
    }tree[M<<2];
    void init(){
        tot=cnt=0;
        memset(head,-1,sizeof(head));
    }
    void add(int u,int v){   
        e[++cnt].v=v;e[cnt].next=head[u];
        head[u]=cnt;
    }
    
    void dfs1(int u,int fa,int d){
        sz[u]=1,son[u]=-1,f[u]=fa,dep[u]=d;
        for(int i=head[u];~i;i=e[i].next){     
            int v=e[i].v;
            if(v==fa) continue;
            dfs1(v,u,d+1);     //继续向下递归
            sz[u]+=sz[v];       
            if(son[u]==-1||sz[v]>sz[son[u]]) son[u]=v;    //找到该节点的重儿子
        }
        return ;
    }
    
    void dfs2(int u,int t){
        tid[u]=++tot; //id记录该节点重新编号后的序号
        rnk[tot]=u;   //线段树中编号对应的原节点编号
        top[u]=t;     //记录下该节点所在重链的链首
        if(son[u]==-1) return;
        dfs2(son[u],t);     //将重边连成重链
        for(int i=head[u];~i;i=e[i].next){
            int v=e[i].v;
            if(v==f[u]||v==son[u]) continue;
            dfs2(v,v);   //找出以轻儿子为链首的重链
        }
        return;
    }
    void Pushup(int rt){
        tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;
    }
    
    void Pushdown(int rt){
        if(tree[rt].lazy){
            int v=tree[rt].lazy;
            tree[rt].lazy=0;
            tree[rt<<1].lazy+=v;
            tree[rt<<1|1].lazy+=v;
            tree[rt<<1].sum+=v*(tree[rt<<1].r-tree[rt<<1].l+1);
            tree[rt<<1|1].sum+=v*(tree[rt<<1|1].r-tree[rt<<1|1].l+1);
        }
    }
    
    void build(int l,int r,int rt){
        tree[rt].l=l;tree[rt].r=r;tree[rt].lazy=0;
        if(l==r){
            tree[rt].sum=a[rnk[l]];
            return;
        }
        int mid=(l+r)>>1;
        build(Lson);
        build(Rson);
        Pushup(rt);
    }
    void update(int L,int R,int l,int r,int rt,int v){
        if(L<=l&&r<=R){
            tree[rt].lazy+=v;
            tree[rt].sum+=v*(r-l+1);
            return ;
        }
        Pushdown(rt);
        int mid=(l+r)>>1;
        if(L<=mid) update(L,R,Lson,v);
        if(R>mid) update(L,R,Rson,v);
        Pushup(rt);
    }
    void query(int l,int r,int rt){
        if(l==r){
            ans=tree[rt].sum;
            return ;
        }
        Pushdown(rt);
        int mid=(l+r)>>1;
        if(pos<=mid) query(Lson);
        if(pos>mid) query(Rson);
        return;
    }
    
    void updates(int x,int y,int v){    
        int fx=top[x],fy=top[y];    //x,y所在重链的链首
        while(fx!=fy){//如果这两个点不在一条重链上则一直向上跳,并且不断更新
            if(dep[fx]>dep[fy]){      //fx节点深度更深
                update(tid[fx],tid[x],1,n,1,v);   //更新这一连续区间时,要用该节点在线段树中的编号
                x=f[fx],fx=top[x];     //从这条重链爬到父节点所在重链的链首上去
            }
            else{  //同理
                update(tid[fy],tid[y],1,n,1,v);
                y=f[fy],fy=top[y];
            }
        }
        if(dep[x]<dep[y])  //在一条重链中时,直接在线段树中将这一段连续的区间更新
            update(tid[x],tid[y],1,n,1,v);
        else
            update(tid[y],tid[x],1,n,1,v);
    }
    
    int main(){
        while(~scanf("%d%d%d",&n,&m,&q)){
            init();
            for(int i=1;i<=n;i++) scanf("%d",&a[i]);
            for(int i=1;i<=m;i++){
                int u,v;
                scanf("%d%d",&u,&v);
                add(u,v);add(v,u);    //链式前向星存下该无向图
            }
            dfs1(1,-1,1);
            dfs2(1,1);
            build(1,n,1);
            while(q--){
                char c[2];
                int c1,c2,k;
                scanf("%s",c);
                if(c[0]=='I'||c[0]=='D'){
                    scanf("%d%d%d",&c1,&c2,&k);
                    if(c[0]=='D') k*=-1;
                    updates(c1,c2,k);
                }
                else{
                    scanf("%d",&c1);
                    pos=tid[c1];     //pos为c1在线段树中的编号
                    query(1,n,1);
                    printf("%d
    ",ans);
                }
            }
        }
        return 0;
    }

      

      

    2018-09-09

  • 相关阅读:
    阶乘递归实现
    队列
    1+2+3+...+100用递归实现
    快速排序C语言实现
    js的onfocus,onblur事件
    CSP2021 游记 菜到离谱
    700题复习计划
    [传递闭包] P2881 [USACO07MAR]排名的牛Ranking the Cows
    【笔记】序列分块
    【题解】UVA10930 A-Sequence
  • 原文地址:https://www.cnblogs.com/00isok/p/9614253.html
Copyright © 2011-2022 走看看