记得以前是用链剖+线段树做的,在链剖的时候顺便维护dfs序,思路简单实现起来就有点麻烦了。。
可参照:http://www.cnblogs.com/onlyRP/p/5041702.html
回顾这题发现其实主要是2操作针对子树而3操作针对链,而且唯一的询问操作3的链其实是到根的权值和,那么就想能不能直接dfs序进行操作呢?做一遍dfs序后我们可以用前缀和求出权值和,操作1修改点权值就把贡献限制在子树内就可以,然而修改树权值就感觉没什么思路了。。操作2对每个结点贡献是t*(d[x]-d[f]),也就是每个结点修改的还不一样,无法统一修改。。
上网找题解发现可以用2个BIT维护,对子树中的结点贡献还可以化为t*d[x]-t*d[f],由于t*d[f]恒定,所以可以类似操作1直接修改点权值,剩下的就再开一个BIT维护_t就可以了,询问的时候直接乘上d[x]即可。。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define inf 1000000007
#define eps 1e-8
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define succ(x) (1<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define ls T[i<<1]
#define rs T[i<<1|1]
#define op T[i]
#define NM 100005
#define nm 200005
#define pi 3.141592653
using namespace std;
int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f*x;
}
struct edge{int t;edge*next;}e[nm],*h[NM],*o=e;
void add(int x,int y){o->t=y;o->next=h[x];h[x]=o++;}
ll a[2][NM],_y;
int in[NM],out[NM],d[NM],tot;
int n,m,_x,_t,b[NM];
void mod(int x,int k,ll t){while(x<=n)a[k][x]+=t,x+=lowbit(x);}
ll sum(int x,int k){return x?a[k][x]+sum(x-lowbit(x),k):0;}
void dfs(int x){
in[x]=++tot;
link(x)if(!in[j->t]){d[j->t]=d[x]+1;dfs(j->t);}
out[x]=tot;
}
int main(){
freopen("data.in","r",stdin);
n=read();m=read();
inc(i,1,n)b[i]=read();
inc(i,1,n-1){_x=read();_y=read();add(_x,_y);add(_y,_x);}
dfs(1);
inc(i,1,n)mod(in[i],0,b[i]),mod(out[i]+1,0,-b[i]);
while(m--){
_t=read();_x=read();
if(_t==3)printf("%lld\n",sum(in[_x],0)+sum(in[_x],1)*(d[_x]+1));
else{
_y=read();
if(_t==1)mod(in[_x],0,_y),mod(out[_x]+1,0,-_y);
else{
mod(in[_x],1,_y);mod(out[_x]+1,1,-_y);
mod(in[_x],0,-_y*d[_x]);mod(out[_x]+1,0,_y*d[_x]);
}
}
}
return 0;
}
4034: [HAOI2015]树上操作
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 6230 Solved: 2061
[ Submit][ Status][ Discuss]
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 。