$ \Rightarrow $ 戳我进BZOJ原题 $ \Rightarrow $ 戳我进洛谷原题
有一棵点数为 $ 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 \quad a )$ 。
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。
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
6
9
13
对于 $ 100 $ % 的数据, $ N,M \le 100000 $ ,且所有输入数据的绝对值都不会超过 $ 10^6 $ 。
(好吧这确实是链剖裸题)
不用链剖的思路:
先搞出来 $ DFS $ 序(要有入栈和出栈两种状态的)
处理出来 线段树区间有多少入栈和多少出栈的
加区间的时候就加(入-出) $ \times 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\n",query(1,1,tim,1,in[x]));
}
return 0;
}
BZOJ 4043 [HAOI2015]树上操作 dfs序 线段树
原文:https://www.cnblogs.com/PotremZ/p/9607310.html