BZOJ4034 树上操作

题目链接:https://vjudge.net/problem/HYSBZ-4034

知识点:  欧拉序列、线段树

解题思路:

  先用 $DFS$ 预处理出树的欧拉序列。对于每一个点,如果它是第一次遍历到的,那么就置其 $flag[] = +1$;如果是在回溯时遍历到的,则置其 $flag[] = -1$。如此一来,当执行操作$3$,“询问某个节点 $x$ 到根的路径中所有点的点权和”时,只需查询欧拉序列中 $x$ 首次出现的序号及其之前是所有的点权乘上其相应的 flag 即为答案。

  对于操作$1$,直接将 $x$ 的两个欧拉序列号 $id[x][0], id[x][1]$ 所对应的点加上 $a$ 乘相应的 $flag$ 的值即可;

  对于操作$2$,则将 $[ id[x][0], id[x][1] ]$ 中的所有点加上 $a$ 乘相应的 $flag$ 的值;

  具体操作请看代码,代码中利用一个 $tag[]$ 来记录区间中所有 $flag[]$ 的和,方便区间加操作;$tree[]$ 记录的是区间和。

AC代码:

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 #define lson l,m,rt<<1
  4 #define rson m+1,r,rt<<1|1
  5 typedef long long LL;
  6 const int MAXN=100000+5;
  7 
  8 vector<int> G[MAXN];
  9 int pos[MAXN<<1],cnt;
 10 int id[MAXN][2];
 11 LL val[MAXN],flag[MAXN<<1];
 12 LL tree[MAXN<<3],tag[MAXN<<3],lazy[MAXN<<3];
 13 
 14 void dfs(int rt,int last){
 15     cnt++;
 16     pos[cnt]=rt,flag[cnt]=1;
 17     if(!id[rt][0])  id[rt][0]=cnt;
 18     for(int i=0;i<G[rt].size();i++){
 19         int v=G[rt][i];
 20         if(v!=last)
 21             dfs(v,rt);
 22     }
 23     cnt++;
 24     pos[cnt]=rt,flag[cnt]=-1;
 25     id[rt][1]=cnt;
 26 }
 27 void pushup(int rt){
 28     tree[rt]=tree[rt<<1]+tree[rt<<1|1];
 29 }
 30 void pushdown(int rt){
 31     tree[rt<<1]+=lazy[rt]*tag[rt<<1];
 32     tree[rt<<1|1]+=lazy[rt]*tag[rt<<1|1];
 33     lazy[rt<<1]+=lazy[rt],lazy[rt<<1|1]+=lazy[rt];
 34     lazy[rt]=0;
 35 }
 36 void build(int l,int r,int rt){
 37     if(l==r){
 38         tree[rt]=flag[l]*val[pos[l]];
 39         tag[rt]=flag[l];
 40         return;
 41     }
 42     int m=(l+r)/2;
 43     build(lson);    build(rson);
 44     tag[rt]=tag[rt<<1]+tag[rt<<1|1];
 45     pushup(rt);
 46 }
 47 void update(int L,int R,LL val,int l,int r,int rt){
 48     if(L<=l&&r<=R){
 49         lazy[rt]+=val;
 50         tree[rt]+=tag[rt]*val;
 51         return;
 52     }
 53     if(lazy[rt])    pushdown(rt);
 54     int m=(l+r)/2;
 55     if(L<=m)    update(L,R,val,lson);
 56     if(R>m)    update(L,R,val,rson);
 57     pushup(rt);
 58 }
 59 LL query(int L,int R,int l,int r,int rt){
 60     if(L<=l&&r<=R)  return tree[rt];
 61     if(lazy[rt])    pushdown(rt);
 62     int m=(l+r)/2;
 63     LL ret=0;
 64     if(L<=m)    ret+=query(L,R,lson);
 65     if(m<R)     ret+=query(L,R,rson);
 66     return ret;
 67 }
 68 
 69 int main(){
 70 //    freopen("in.txt","r",stdin);
 71     int n,m;
 72     scanf("%d%d",&n,&m);
 73     for(int i=1;i<=n;i++)   scanf("%lld",&val[i]);
 74     for(int i=1;i<n;i++){
 75         int u,v;
 76         scanf("%d%d",&u,&v);
 77         G[u].push_back(v);
 78         G[v].push_back(u);
 79     }
 80     cnt=0;
 81     dfs(1,-1);
 82     build(1,cnt,1);
 83 
 84     while(m--){
 85         int od,x;
 86         LL a;
 87         scanf("%d",&od);
 88         if(od==1){
 89             scanf("%d%lld",&x,&a);
 90             update(id[x][0],id[x][0],a,1,cnt,1);
 91             update(id[x][1],id[x][1],a,1,cnt,1);
 92         }
 93         else if(od==2){
 94             scanf("%d%lld",&x,&a);
 95             update(id[x][0],id[x][1],a,1,cnt,1);
 96         }
 97         else{
 98             scanf("%d",&x);
 99             printf("%lld
",query(1,id[x][0],1,cnt,1));
100         }
101     }
102     return 0;
103 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”
原文地址:https://www.cnblogs.com/Blogggggg/p/9583924.html