BZOJ 4034 [HAOI2015]树上操作 树链剖分

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 。


题目链接


啊,终于在bzoj又找到一题水题!

此题都不需要思考,,就是树链剖分的模板题目了。

我们知道树链剖分中,为了让线段树能够维护,我们给了每个节点一个dfs序的编号。

要不下次写一篇树链剖分

所以为了更新子树,我们还需要记录一下每一个结点u的子树中dfs序最大元素的标号。

总而言之就是模板,没什么好说的。。。


哦哦,对了!

坑点就是要开long long的。


#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while (ch<'0' || ch>'9'){if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int
	N=100005;
int Ecnt,cnt,n,m;
ll a[N];
struct Tree{
	int sz,top,pre,son,tid,Max;
}tree[N];
struct Segment{
	ll mark,sum;
}Seg[N<<2];
struct Edge{
	int next,to;
}E[N<<1]; int head[N];
inline void add(int u,int v){
	E[++Ecnt].next=head[u];
	E[Ecnt].to=v;
	head[u]=Ecnt;
}
inline void up(int u){
	Seg[u].sum=Seg[u<<1].sum+Seg[u<<1|1].sum;
}
inline void down(int u,int l,int r){
	if (Seg[u].mark){
		int mid=(l+r)>>1;
		Seg[u<<1].mark+=Seg[u].mark;
		Seg[u<<1|1].mark+=Seg[u].mark;
		Seg[u<<1].sum+=Seg[u].mark*(ll)(mid-l+1);
		Seg[u<<1|1].sum+=Seg[u].mark*(ll)(r-mid);
		Seg[u].mark=(ll)0;
	}
}
void build(int x,int pre){
	tree[x].sz=1;	tree[x].pre=pre;
	tree[x].son=0;
	int maxx=0;
	for (int i=head[x];i;i=E[i].next){
		int j=E[i].to;
		if (j==pre) continue;
		build(j,x);
		if (maxx<tree[j].sz){
			maxx=tree[j].sz;
			tree[x].son=j;
		}
		tree[x].sz+=tree[j].sz;
	}
}
void dfs(int x,int ancestor){
	tree[x].top=ancestor;
	tree[x].tid=tree[x].Max=++cnt;
	if (tree[x].son) dfs(tree[x].son,ancestor);
	tree[x].Max=max(tree[x].Max,tree[tree[x].son].Max);
	for (int i=head[x];i;i=E[i].next){
		int j=E[i].to;
		if (j==tree[x].pre || j==tree[x].son) continue;
		dfs(j,j);
		tree[x].Max=max(tree[x].Max,tree[j].Max);
	}
}
void update(int id,int l,int r,int gl,int gr,ll num){
	if (l>=gl && r<=gr){
		Seg[id].mark+=num;
		Seg[id].sum+=num*(ll)(r-l+1);
		down(id,l,r);
		return;
	}
	down(id,l,r);
	int mid=(l+r)>>1;
	if (gr<=mid) update(id<<1,l,mid,gl,gr,num);	else
	if (gl>mid)  update(id<<1|1,mid+1,r,gl,gr,num);
		else{
			update(id<<1,l,mid,gl,mid,num);
			update(id<<1|1,mid+1,r,mid+1,gr,num);
		}
	up(id);
}
ll query(int id,int l,int r,int gl,int gr){
	down(id,l,r);
	if (l>=gl && r<=gr) return Seg[id].sum;
	int mid=(l+r)>>1;
	if (gr<=mid) return query(id<<1,l,mid,gl,gr);	else
	if (gl>mid)  return query(id<<1|1,mid+1,r,gl,gr);
		else
			return query(id<<1,l,mid,gl,mid)+query(id<<1|1,mid+1,r,mid+1,gr);
	up(id);
}
ll solve(int x){
	ll sum=(ll)0;
	while (x!=0){
		sum+=query(1,1,n,tree[tree[x].top].tid,tree[x].tid);
		x=tree[tree[x].top].pre;
	}
	return sum;
}
int main(){
	memset(Seg,0,sizeof(Seg));
	n=read(),m=read();
	for (int i=1;i<=n;i++)	a[i]=read();
	Ecnt=0;	int x,y;
	for (int i=1;i<n;i++){
		x=read(),y=read();
		add(x,y); add(y,x);
	}
	build(1,0);
	cnt=0;	dfs(1,0);
	for (int i=1;i<=n;i++)
		update(1,1,n,tree[i].tid,tree[i].tid,a[i]);
	int opt; ll q; 
	while (m--){
		opt=read();
		if (opt==1){
			x=read(),q=read();
			update(1,1,n,tree[x].tid,tree[x].tid,q);
		}	else
		if (opt==2){
			x=read(),q=read();
			update(1,1,n,tree[x].tid,tree[x].Max,q);
		}	else{
			x=read();
			printf("%lld\n",solve(x));
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值