BZOJ 4043 [HAOI2015]树上操作 dfs序 线段树

$ Rightarrow $ 戳我进BZOJ原题 $ Rightarrow $ 戳我进洛谷原题

[HAOI2015]树上操作

Time Limit: 10 Sec Memory Limit: 256 MB
 

Description

有一棵点数为 $ N $ 的树,以点 $ 1 $ 为根,且树点有边权。然后有 $ M $ 个操作,分为三种:
 
操作 $ 1 $ :把某个节点 $ x $ 的点权增加 $ a $ 。
操作 $ 2 $ :把某个节点 $ x $ 为根的子树中所有点的点权都增加 $ a $ 。
操作 $ 3 $ :询问某个节点 $ x $ 到根的路径中所有点的点权和。
 

Input

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

Output

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

Sample Input

 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

Sample Output

 6
 9
 13

 

HINT

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

思路

  • (好吧这确实是链剖裸题)

  • 不用链剖的思路:

  • 先搞出来 $ DFS $ 序(要有入栈和出栈两种状态的)

  • 处理出来 线段树区间有多少入栈和多少出栈的

  • 加区间的时候就加(入-出) $ imes wei $

  • 查前缀和( $ 1 $ 到 $ in[x] $ )
     

代码

/**************************************************************
    Problem: 4034
    User: PotremZ
    Language: C++
    Result: Accepted
    Time:3968 ms
    Memory:50212 kb
****************************************************************/
 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define N 100005
#define int long long
vector<int>e[N];
int n,m,w[N],id[N<<1],in[N],out[N],tim;
void dfs(int u,int fa){
    id[in[u]=++tim]=u;
    for(int i=0;i<e[u].size();++i){
        int v=e[u][i];
        if(v==fa) continue;
        dfs(v,u); 
    }
    id[out[u]=++tim]=u;
}
int sum[N<<4],flg[N<<4],lzy[N<<4];
void build(int o,int l,int r){
    if(l==r){
        if(in[id[l]]==l){ sum[o]=w[id[l]]; flg[o]=1; }
        else{ sum[o]=-w[id[l]]; flg[o]=-1; }
        return;
    }
    int mid=l+r>>1;
    build(o<<1,l,mid); build(o<<1|1,mid+1,r);
    sum[o]=sum[o<<1]+sum[o<<1|1];
    flg[o]=flg[o<<1]+flg[o<<1|1];
}
void pushdown(int o){
    sum[o<<1]+=flg[o<<1]*lzy[o];
    sum[o<<1|1]+=flg[o<<1|1]*lzy[o];
    lzy[o<<1]+=lzy[o];
    lzy[o<<1|1]+=lzy[o];
    lzy[o]=0;
}
void updata(int o,int l,int r,int L,int R,int val){
    if(L<=l&&r<=R){
        sum[o]+=flg[o]*val;
        lzy[o]+=val;
        return;
    }
    if(lzy[o]) pushdown(o);
    int mid=l+r>>1;
    if(L>mid) updata(o<<1|1,mid+1,r,L,R,val);
    else if(R<=mid) updata(o<<1,l,mid,L,R,val);
    else {
        updata(o<<1,l,mid,L,R,val);
        updata(o<<1|1,mid+1,r,L,R,val);
    }
    sum[o]=sum[o<<1]+sum[o<<1|1];   
}
int query(int o,int l,int r,int L,int R){
    if(L<=l&&r<=R) return sum[o];
    if(lzy[o]) pushdown(o);
    int mid=l+r>>1;
    if(L>mid) return query(o<<1|1,mid+1,r,L,R);
    else if(R<=mid) return query(o<<1,l,mid,L,R);
    else return query(o<<1,l,mid,L,R)+query(o<<1|1,mid+1,r,L,R);
}
signed main(){
    scanf("%lld %lld",&n,&m);
    for(int i=1;i<=n;++i) scanf("%lld",&w[i]);
    for(int i=1;i<n;++i){
        int u,v;
        scanf("%lld %lld",&u,&v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(1,0);
    build(1,1,tim);
    while(m--){
        int opt,x;
        scanf("%lld %lld",&opt,&x);
        if(opt==1){
            int a;
            scanf("%lld",&a);
            updata(1,1,tim,in[x],in[x],a);
            updata(1,1,tim,out[x],out[x],a);
        } else if(opt==2){
            int a;
            scanf("%lld",&a);
            updata(1,1,tim,in[x],out[x],a);
        } else printf("%lld
",query(1,1,tim,1,in[x]));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/PotremZ/p/BZOJ4043.html