Description
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个
操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
Input
第一行包含两个整数 N, M 。表示点数和操作数。
接下来一行 N 个整数,表示树中节点的初始权值。
接下来 N-1 行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。
再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操
作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x 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
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
9
13
HINT
对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不
会超过 10^6 。
Source
一开始想用树链剖分,结果发现只需要维护dfs序就可以了
我用的是线段树维护。。没去想怎么用树状数组维护
首先dfs记录进入的时刻和弹出的时刻。进入权值为正,弹出为负
然后要查询路径的话直接查询1到x的权值和就可以了
单点修改就直接线段树上修改
子树就该即修改进入和弹出中间的那一段
每段维护正权值点和以及负权值点和。最后减一下就可以了
*记得开long long
#include<cstdio>
using namespace std;
struct line
{
int s,t;
int next;
}a[200001];
int head[100001];
int edge;
inline void add(int s,int t)
{
a[edge].next=head[s];
head[s]=edge;
a[edge].s=s;
a[edge].t=t;
}
int w[200001],b[200001];
int ld[200001],rd[200001];
bool v[200001];
int tot;
struct tree
{
int l,r;
long long s1,s2;
long long x1,x2;
long long tag;
}tr[800001];
inline void up(int p)
{
tr[p].s1=tr[p*2].s1+tr[p*2+1].s1;
tr[p].x1=tr[p*2].x1+tr[p*2+1].x1;
tr[p].s2=tr[p*2].s2+tr[p*2+1].s2;
tr[p].x2=tr[p*2].x2+tr[p*2+1].x2;
}
inline void down(int p)
{
long long ll1,ll2,lr1,lr2;
long long x=tr[p].tag;
ll1=tr[p*2].x1;
ll2=tr[p*2].x2;
lr1=tr[p*2+1].x1;
lr2=tr[p*2+1].x2;
tr[p*2].s1+=ll1*x;
tr[p*2].s2+=ll2*x;
tr[p*2+1].s1+=lr1*x;
tr[p*2+1].s2+=lr2*x;
tr[p*2].tag+=x;
tr[p*2+1].tag+=x;
tr[p].tag=0;
}
inline void build(int p,int l,int r)
{
tr[p].l=l;
tr[p].r=r;
if(l!=r)
{
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
up(p);
}
else
{
if(w[l]>0)
{
tr[p].s1=b[w[l]];
tr[p].x1=1;
}
else
{
tr[p].s2=b[-w[l]];
tr[p].x2=1;
}
}
}
inline void add1(int p,int l,int r,long long x)
{
if(l<=tr[p].l&&tr[p].r<=r)
{
if(tr[p].x1==1)
tr[p].s1+=x;
else
tr[p].s2+=x;
}
else
{
down(p);
int mid=(tr[p].l+tr[p].r)/2;
if(l<=mid)
add1(p*2,l,r,x);
if(r>mid)
add1(p*2+1,l,r,x);
up(p);
}
}
inline void add2(int p,int l,int r,long long x)
{
if(l<=tr[p].l&&tr[p].r<=r)
{
long long l1=tr[p].x1,l2=tr[p].x2;
tr[p].s1+=x*l1;
tr[p].s2+=x*l2;
tr[p].tag+=x;
}
else
{
down(p);
int mid=(tr[p].l+tr[p].r)/2;
if(l<=mid)
add2(p*2,l,r,x);
if(r>mid)
add2(p*2+1,l,r,x);
up(p);
}
}
tree nw;
inline tree ask(int p,int l,int r)
{
if(l<=tr[p].l&&tr[p].r<=r)
return tr[p];
else
{
down(p);
int mid=(tr[p].l+tr[p].r)/2;
tree ans1=nw,ans2=nw,ans=nw;
bool flag1=false,flag2=false;
if(l<=mid)
{
flag1=true;
ans1=ask(p*2,l,r);
}
if(r>mid)
{
flag2=true;
ans2=ask(p*2+1,l,r);
}
if(flag1)
{
if(flag2)
{
ans.s1=ans1.s1+ans2.s1;
ans.s2=ans1.s2+ans2.s2;
ans.x1=ans1.x1+ans2.x1;
ans.x2=ans1.x2+ans2.x2;
}
else
ans=ans1;
}
else
ans=ans2;
return ans;
}
}
inline void dfs(int d)
{
tot++;
ld[d]=tot;
w[tot]=d;
v[d]=true;
int i;
for(i=head[d];i!=0;i=a[i].next)
{
int t=a[i].t;
if(!v[t])
dfs(t);
}
tot++;
rd[d]=tot;
w[tot]=-d;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
int i;
for(i=1;i<=n;i++)
scanf("%d",&b[i]);
int s,t;
for(i=1;i<=n-1;i++)
{
scanf("%d%d",&s,&t);
edge++;
add(s,t);
edge++;
add(t,s);
}
dfs(1);
build(1,1,tot);
int x;
for(i=1;i<=m;i++)
{
scanf("%d",&x);
if(x==1)
{
scanf("%d%d",&s,&t);
add1(1,ld[s],ld[s],t);
add1(1,rd[s],rd[s],t);
}
else if(x==2)
{
scanf("%d%d",&s,&t);
add2(1,ld[s],rd[s],t);
}
else
{
scanf("%d",&s);
tree xt=ask(1,1,ld[s]);
printf("%lld\n",xt.s1-xt.s2);
}
}
return 0;
}