4034: [HAOI2015]树上操作
Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 4744 Solved: 1513
[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
Sample Output
6
9
13
HINT
对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。
这道题显然是一道树链剖分裸题,有一点值得注意,就是第二个操作的处理,其实我们可以发现,对于一棵子树的修改,我们可以发现,这棵子树中所有点的序号是连续的,所以我们可以记录一个该子树中序号最大的点,就可以进行操作了
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#define dnt long long
const int MAXN = 100000+10;
using namespace std;
int readin(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
dnt val[MAXN];
struct Line{
int from,to,nxt;
}line[MAXN*2+10];
int N,M,tail;
int head[MAXN];
int mx[MAXN],top[MAXN],dep[MAXN],siz[MAXN],son[MAXN],fa[MAXN],tid[MAXN],timer,rank[MAXN];
dnt sum[MAXN*4+10],mark[MAXN*4+10];
void add_line(int from,int to){
tail++;
line[tail].from=from;
line[tail].to=to;
line[tail].nxt=head[from];
head[from]=tail;
}
void dfs1(int u,int father,int d){
fa[u]=father;
dep[u]=d;
siz[u]=1;
for(register int i=head[u];i;i=line[i].nxt){
int v=line[i].to;
if(v!=father){
dfs1(v,u,d+1);
siz[u]+=siz[v];
if(son[u]==-1||siz[son[u]]<siz[v]) son[u]=v;
}
}
}
void dfs2(int u,int tp){
tid[u]=mx[u]=++timer;
rank[tid[u]]=u;
top[u]=tp;
if(son[u]==-1) return;
dfs2(son[u],tp);mx[u]=max(mx[u],mx[son[u]]);
for(register int i=head[u];i;i=line[i].nxt){
int v=line[i].to;
if(v!=son[u]&&v!=fa[u]){
dfs2(v,v);
mx[u]=max(mx[u],mx[v]);
}
}
}
void pushup(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void pushdown(int rt,int len){
if(mark[rt]){
mark[rt<<1]+=mark[rt];
mark[rt<<1|1]+=mark[rt];
sum[rt<<1]+=(len-(len>>1))*mark[rt];
sum[rt<<1|1]+=(len>>1)*mark[rt];
mark[rt]=0;
}
}
void build(int l,int r,int rt){
mark[rt]=0;
if(l==r){
sum[rt]=val[rank[l]];
return;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pushup(rt);
}
void modify(int L,int R,dnt add,int l,int r,int rt){
if(L<=l&&R>=r){
mark[rt]+=add;
sum[rt]+=(r-l+1)*add;
return;
}
pushdown(rt,r-l+1);
int mid=(l+r)>>1;
if(L<=mid) modify(L,R,add,l,mid,rt<<1);
if(R>mid) modify(L,R,add,mid+1,r,rt<<1|1);
pushup(rt);
}
dnt query(int L,int R,int l,int r,int rt){
if(L<=l&&R>=r){
return sum[rt];
}
pushdown(rt,r-l+1);
int mid=(l+r)>>1;
dnt sumn=0;
if(L<=mid) sumn+=query(L,R,l,mid,rt<<1);
if(R>mid) sumn+=query(L,R,mid+1,r,rt<<1|1);
return sumn;
}
dnt _query(int from,int to){
dnt ans=0;
while(top[from]!=top[to]){
if(dep[top[from]]<dep[top[to]]) swap(from,to);
ans+=query(tid[top[from]],tid[from],1,N,1);
from=fa[top[from]];
}
if(dep[from]<dep[to]) swap(from,to);
ans+=query(tid[to],tid[from],1,N,1);
return ans;
}
int main(){
freopen("BZOJ4034.txt","r",stdin);
//freopen(".out","w",stdout);
int f,t;
memset(son,-1,sizeof(son));
scanf("%d%d",&N,&M);
for(register int i=1;i<=N;i++) scanf("%lld",&val[i]);
for(register int i=1;i<=N-1;i++){
scanf("%d%d",&f,&t);
add_line(f,t);
add_line(t,f);
}
dfs1(1,0,0);
dfs2(1,1);
build(1,N,1);
for(register int i=1;i<=M;i++){
int opt,te;
dnt mp;
scanf("%d",&opt);
if(opt==1){
scanf("%d%lld",&te,&mp);
modify(tid[te],tid[te],mp,1,N,1);
}else if(opt==2){
scanf("%d%lld",&te,&mp);
modify(tid[te],mx[te],mp,1,N,1);
}else{
scanf("%d",&te);
mp=_query(1,te);
printf("%lld\n",mp);
}
}
return 0;
}