zoukankan      html  css  js  c++  java
  • [HAOI2015]树上操作 题解

    题目描述

    有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:

    • 操作 1 :把某个节点 x 的点权增加 a 。
    • 操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
    • 操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

    输入格式

    第一行包含两个整数 N, M 。表示点数和操作数。
    接下来一行 N 个整数,表示树中节点的初始权值。
    接下来 N-1 行每行两个正整数 from, to , 表示该树中存在一条边 (from, to) 。
    再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。

    输出格式

    对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

    输入输出样例

    输入 #1
    5 5
    1 2 3 4 5
    1 2
    1 4
    2 3
    2 5
    3 3
    1 2 1
    3 5
    2 1 2
    3 3
    输出 #1
    6
    9
    13

    说明/提示

    对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。


    一道树链剖分模板题。。。,比洛谷树链剖分模板题还简单

    【模板】树链剖分需要支持路径修改子树修改子树查询路径查询

    这道题只需要支持单点修改子树修改路径查询,而且路径的左端点还固定为1,其实这道题应该是蓝题的。。。

    哦对了,重要的事情说三遍:

    开long long开long long开long long

    不会树链剖分的小伙伴可以参考以下博客

    博客1

    博客2

    想联系树链剖分的同学们也可以参考以下题目

     [NOI2015]软件包管理器

    【模板】树链剖分

     [SDOI2011]染色

    好了废话不多数,放代码吧

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    struct SYM{
        int to,next;
    }edge[200010];
    struct ASJ{
        long long  sum;
        long long lz;
    }tree[400010];
    int head[100010],tot;
    int n,m;
    int w[100010],dep[100010],fa[100010],son[100010],siz[100010],top[100010],wet[100010],id[100010];
    void addedge(int x,int y){
        edge[++tot].to=y;
        edge[tot].next=head[x];
        head[x]=tot;
    }
    void build(int i,int l,int r){                          //建树 
        if(l==r){
            tree[i].sum=wet[l];
            return ;
        }
        int mid=(l+r)/2;
        build(2*i,l,mid);                                   //左儿子 
        build(2*i+1,mid+1,r);                               //右儿子 
        tree[i].sum=(tree[2*i].sum+tree[2*i+1].sum);
    }
    void pushdown(int i,long long len){                    //LAZY下传 
        tree[2*i].lz+=tree[i].lz;
        tree[2*i+1].lz+=tree[i].lz;
        tree[2*i].sum+=(tree[i].lz*(len-len/2));
        tree[2*i+1].sum+=(tree[i].lz*(len/2));
        tree[i].lz=0;                                      //别忘了清零 
    }
    void update(int i,int l,int r,int L,int R,long long k){//更新操作 
        if(l>=L&&r<=R){
            tree[i].sum+=k*(r-l+1);       
            tree[i].lz+=k;
            return ;
        }
        int mid=(l+r)/2;
        pushdown(i,(r-l+1));                               //下传LAZY 
        if(L<=mid) update(2*i,l,mid,L,R,k);
        if(R>mid) update(2*i+1,mid+1,r,L,R,k);
        tree[i].sum=tree[2*i].sum+tree[2*i+1].sum;
    }
    long long query(int i,int l,int r,int L,int R){//查询操作 
        long long ans=0;
        if(l>=L&&r<=R){
            return tree[i].sum;
        }
        int mid=(l+r)/2;
        pushdown(i,(r-l+1));
        if(L<=mid) ans+=query(2*i,l,mid,L,R);
        if(R>=mid+1) ans+=query(2*i+1,mid+1,r,L,R);
        return ans;
    }
    //----------------------------------------------------------------上面是线段树 
    void dfs1(int now,int from){               //处理dep,fa,siz,以及重儿子son 
        dep[now]=dep[from]+1;
        fa[now]=from;
        int maxson=-1;
        siz[now]=1;
        for(int i=head[now];i;i=edge[i].next){
            int v=edge[i].to;
            if(v==from) continue;
            dfs1(v,now);
            siz[now]+=siz[v];
            if(siz[v]>maxson){
                son[now]=v;
                maxson=siz[v];
            }
        }
    }
    int cnt;
    void dfs2(int now,int topr){              //处理重链链顶top,新点id,新点权值wet 
        id[now]=++cnt;
        top[now]=topr;
        wet[cnt]=w[now];
        if(!son[now]) return;
        dfs2(son[now],topr);                  //先处理重儿子,再处理轻儿子 
        for(int i=head[now];i;i=edge[i].next){
            int v=edge[i].to;
            if(v==fa[now]||v==son[now]) continue;
            dfs2(v,v);                        //每个轻儿子都是一个新的链顶,别忘了换链顶!!! 
        }
    }
    void update1(int x,int k){
        update(1,1,n,id[x],id[x]+siz[x]-1,k); //子树是连续的所以左节点id[x],右节点id[x]+siz[x]-1 
    }
    long long q1(int x,int y){               //这里我写的有点麻烦,因为一个点固定为根1,所以其实可以省略一些,不过这里的代码是可以应用于每一个树链剖分路经查询的 
        long long ans=0;
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]]) swap(x,y);
            ans+=query(1,1,n,id[top[x]],id[x]);
            x=fa[top[x]];
        }
        if(dep[x]>dep[y]) swap(x,y);
        ans+=query(1,1,n,id[x],id[y]);
        return ans;
    }
    int main(){
        freopen("sscz.in","r",stdin);
        freopen("sscz.out","w",stdout);
        int no,x,y;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
            scanf("%d",&w[i]);
        for(int i=1;i<n;i++){
            scanf("%d%d",&x,&y);
            addedge(x,y);
            addedge(y,x);
        }
        dfs1(1,0);
        dfs2(1,1);
        build(1,1,n);
        while(m--){
            scanf("%d",&no);
            if(no==1){
                scanf("%d%d",&x,&y);
                update(1,1,n,id[x],id[x],y);                 //单点修改 
            }
            if(no==2){
                scanf("%d%d",&x,&y);                        //子树修改 
                update1(x,y);
            }
            if(no==3){                                      //路径查询 
                scanf("%d",&x);
                printf("%lld
    ",q1(x,1));
            }
        }
    }
    自己选择的路,跪着也要走完
  • 相关阅读:
    2013 ACM/ICPC Asia Regional Changsha Online G Goldbach
    【转】海量数据求中位数
    【转】如何在vmware中如何设置ip
    十大基本功之testbench
    图像处理与计算机视觉:基础,经典以及最近发展
    Quartus II调用modelsim无缝仿真
    ise和quartus共用仿真软件
    modesim 仿真问题
    Altium Designer设计PCB中如何开槽
    原理图间的 总线接口
  • 原文地址:https://www.cnblogs.com/tonyshen/p/11369386.html
Copyright © 2011-2022 走看看