4034: [HAOI2015]树上操作
Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 6033 Solved: 1959
[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 。
Source
网上大部分都是裸的树剖,其实可以用dfs序+线段树来写
dfs序就不说了,处理出来进栈时间和出栈时间
对于1操作就是在进栈时间对应线段树上的那个点加上v,在出栈时间对应线段树上的那个点减v
对于2操作就是在x进栈时间在线段树上对应的那个点和出栈时间在线段树上对应的那个点之间的区间中,是进栈时间的就加上v就是出栈时间的就减去v
这个我们用一个flag辅助数组来写,flag为1表示是进栈时间,flag为-1表示是出栈时间
第三个操作我们直接查询1到x进栈时间在线段树上对应那个点的区间和即可
具体实现看代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
ll x=0;ll 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 x*f;
}
const ll MAXN=1e6+10;
struct node{
ll y,next;
}e[MAXN];
ll linkk[MAXN],len=0,n,m,in[MAXN],out[MAXN],dfs_clock=0,a[MAXN],b[MAXN],mk[MAXN];
inline void insert(ll xx,ll yy){
e[++len].y=yy;e[len].next=linkk[xx];linkk[xx]=len;
}
inline void dfs(ll st,ll father){
in[st]=++dfs_clock;
mk[dfs_clock]=1;b[dfs_clock]=a[st];
for(ll i=linkk[st];i;i=e[i].next){
if(e[i].y!=father){
dfs(e[i].y,st);
}
}
out[st]=++dfs_clock;mk[dfs_clock]=-1;b[dfs_clock]=a[st];
}
void init(){
n=read();m=read();
for(ll i=1;i<=n;i++){
a[i]=read();
}
for(ll i=1;i<n;i++){
ll xx=read();ll yy=read();
insert(xx,yy);insert(yy,xx);
}
dfs(1,0);
}
struct Tree{
ll delta,sum,flag;
}T[MAXN<<1];
ll x,y,v;
inline void push_down(ll root){
ll del=T[root].delta;
T[root<<1].delta+=del;T[root<<1|1].delta+=del;
T[root<<1].sum+=del*T[root<<1].flag;T[root<<1|1].sum+=del*T[root<<1|1].flag;
T[root].delta=0;
}
inline void build(ll l,ll r,ll root){
T[root].sum=T[root].delta=0;
ll mid=(l+r)>>1;
if(l==r){
T[root].sum=mk[l]*b[l];
T[root].flag=mk[l];
return;
}
build(l,mid,root<<1);
build(mid+1,r,root<<1|1);
T[root].flag=T[root<<1].flag+T[root<<1|1].flag;
T[root].sum=T[root<<1].sum+T[root<<1|1].sum;
}
inline void insert1(ll l,ll r,ll root){
if(l>x||r<x) return;
if(l==r){
T[root].sum+=T[root].flag*y;
T[root].delta+=y;
return;
}
ll mid=(l+r)>>1;
if(T[root].delta) push_down(root);
insert1(l,mid,root<<1);
insert1(mid+1,r,root<<1|1);
T[root].sum=T[root<<1].sum+T[root<<1|1].sum;
}
inline void insert2(ll l,ll r,ll root){
if(l>y||r<x) return;
if(l>=x&&r<=y){
T[root].sum+=T[root].flag*v;T[root].delta+=v;
return;
}
ll mid=(l+r)>>1;
if(T[root].delta) push_down(root);
insert2(l,mid,root<<1);
insert2(mid+1,r,root<<1|1);
T[root].sum=T[root<<1].sum+T[root<<1|1].sum;
}
inline ll query(ll l,ll r,ll root){
if(l>y||r<x) return 0;
if(l>=x&&r<=y) return T[root].sum;
ll mid=(l+r)>>1;
if(T[root].delta) push_down(root);
return query(l,mid,root<<1)+query(mid+1,r,root<<1|1);
}
void solve(){
build(1,dfs_clock,1);
for(ll i=1;i<=m;i++){
ll op=read();
if(op==1){
ll t=read();y=read();x=in[t];
insert1(1,dfs_clock,1);
x=out[t];
insert1(1,dfs_clock,1);
}
if(op==2){
ll t=read();v=read();
x=in[t];y=out[t];
insert2(1,dfs_clock,1);
}
if(op==3){
ll t=read();x=1;y=in[t];
printf("%lld\n",query(1,dfs_clock,1));
}
}
}
int main(){
//freopen("All.in","r",stdin);
//freopen("a.out","w",stdout);
init();
solve();
return 0;
}