题目:BZOJ4034.
题目大意:给定一棵
n
n
n个节点的树,让你支持3个操作:
1.把某个节点
x
x
x的点权增加
a
a
a.
2.把某个节点
x
x
x为根的子树中所有点的点权都增加
a
a
a.
3.询问某个节点
x
x
x到根的路径中所有点的点权和.
设操作数为
m
m
m,则
1
≤
n
,
m
≤
1
0
5
1\leq n,m\leq 10^5
1≤n,m≤105.
这道题显然是道树剖板子题,但是我们强制用dfs序做呢?
我们把dfs序时一个节点入栈出栈各操作一次,现在我们可以让入栈为正,出栈为负,得到一个 1 1 1和 − 1 -1 −1组成的dfs序.
为什么可以这样做?是因为这样维护 [ 1 , i ] [1,i] [1,i]的总权值就是以 i i i节点到根节点这条链上节点的总权值.
也就是说,一条从 x x x到根节点的链的所有节点,就是这棵树dfs的时候,刚好扫到了 x x x,这是被扫到了却未被回溯过的节点.
用dfs的思路来看这是显然的.
那么怎么维护这棵树呢?
我们可以考虑用dfs序搞下来之后,建树时,若这个元素是入栈顺序,则把flag记为 1 1 1,否则记为 − 1 -1 −1,然后合并.
所以AC代码如下:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N=100000;
int n,m;
LL a[N+1]={0};
struct side{
int y,next;
}e[2*N+1];
int lin[N+1]={0},top=0;
void ins(int X,int Y){
e[++top].y=Y;
e[top].next=lin[X];
lin[X]=top;
}
bool use[N+1]={0};
int dfn[N+1]={0},low[N+1]={0},tt=0;
LL b[2*N+1]={0};
void dfs(int k){
use[k]=1;
dfn[k]=++tt;b[tt]=1LL;
for (int i=lin[k];i;i=e[i].next)
if (!use[e[i].y]) dfs(e[i].y);
low[k]=++tt;b[tt]=-1LL;
}
struct tree{
int l,r;
LL sum,flag,tag;
}tr[N*10];
void build(int L,int R,int k=1){
tr[k].l=L;tr[k].r=R;
tr[k].sum=tr[k].tag=0LL;
if (L==R){
tr[k].flag=b[L];
return;
}
int mid=L+R>>1;
build(L,mid,k<<1);
build(mid+1,R,k<<1|1);
tr[k].flag=tr[k<<1].flag+tr[k<<1|1].flag;
}
void pushdown(int k){
int ls=k<<1,rs=ls|1;
tr[ls].tag+=tr[k].tag;
tr[rs].tag+=tr[k].tag;
tr[ls].sum+=tr[ls].flag*tr[k].tag;
tr[rs].sum+=tr[rs].flag*tr[k].tag;
tr[k].tag=0LL;
}
void add(int L,int R,LL num,int k=1){
if (tr[k].l==L&&tr[k].r==R){
tr[k].sum+=tr[k].flag*num;
tr[k].tag+=num;
return;
}
pushdown(k);
int mid=tr[k].l+tr[k].r>>1;
if (R<=mid) add(L,R,num,k<<1);
else if (L>mid) add(L,R,num,k<<1|1);
else add(L,mid,num,k<<1),add(mid+1,R,num,k<<1|1);
tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum;
}
LL query(int L,int R,int k=1){
if (tr[k].l==L&&tr[k].r==R) return tr[k].sum;
int mid=tr[k].l+tr[k].r>>1;
pushdown(k);
if (R<=mid) return query(L,R,k<<1);
else if (L>mid) return query(L,R,k<<1|1);
else return query(L,mid,k<<1)+query(mid+1,R,k<<1|1);
}
inline void into(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%lld",&a[i]);
int x,y;
for (int i=1;i<n;i++){
scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
}
inline void work(){
dfs(1);
build(1,2*n);
for (int i=1;i<=n;i++)
add(dfn[i],dfn[i],a[i]),add(low[i],low[i],a[i]);
}
inline void outo(){
int x,opt;
LL y;
for (int i=1;i<=m;i++){
scanf("%d",&opt);
switch (opt){
case 1:scanf("%d%lld",&x,&y);
add(dfn[x],dfn[x],y);add(low[x],low[x],y);
break;
case 2:scanf("%d%lld",&x,&y);
add(dfn[x],low[x],y);
break;
case 3:scanf("%d",&x);
printf("%lld\n",query(1,dfn[x]));
break;
}
}
}
int main(){
into();
work();
outo();
return 0;
}