[HAOI2015]树上操作

题目描述
有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个操作,分为三种:

操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
输入格式
第一行包含两个整数 N, M 。表示点数和操作数。
接下来一行 N 个整数,表示树中节点的初始权值。
接下来 N-1 行每行两个正整数 from, to , 表示该树中存在一条边 (from, to) 。
再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。

输出格式
对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

输入输出样例
输入 #1 复制

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 复制
6
9
13
说明/提示
对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不会超过 10^6 。


很明显的树链剖分,也可以考虑 LCT 直接维护就好了,维护区间和,然后对线段树区间更新,怎么修改子树所有权值呢?因为树链剖分具有dfs序一样的性质,子树连续,所以我们只要知道子树的大小即可,然后子树的大小在树链剖分dfs1的时候已经维护了,所以更新即可。


为什么要写这么裸的博客呢?(经常更新博客,证明我还活着!!!)


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int n,m,val[N],h[N],pos[N],bl[N],f[N],sz[N],cnt;
int head[N],nex[N<<1],to[N<<1],tot;
inline void add(int a,int b){
	to[++tot]=b; nex[tot]=head[a]; head[a]=tot;
}
struct node{
	int l,r,add,sum;
}t[N<<2];
void dfs1(int x){
	sz[x]=1;
	for(int i=head[x];i;i=nex[i]){
		if(f[x]==to[i])	continue;
		h[to[i]]=h[x]+1;	f[to[i]]=x;
		dfs1(to[i]);	sz[x]+=sz[to[i]];
	}
}
void dfs2(int x,int belong){
	int k=0;	pos[x]=++cnt;	bl[x]=belong;
	for(int i=head[x];i;i=nex[i])
		if(h[to[i]]>h[x]&&sz[to[i]]>sz[k])	k=to[i];
	if(!k)	return ;	dfs2(k,belong);
	for(int i=head[x];i;i=nex[i])
		if(h[to[i]]>h[x]&&to[i]!=k)	dfs2(to[i],to[i]);
}
inline void push_up(int p){
	t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
}
inline void push_down(int p){
	if(t[p].add){
		t[p<<1].add+=t[p].add;	t[p<<1|1].add+=t[p].add;
		t[p<<1].sum+=(t[p<<1].r-t[p<<1].l+1)*t[p].add;
		t[p<<1|1].sum+=(t[p<<1|1].r-t[p<<1|1].l+1)*t[p].add;
		t[p].add=0;
	}
}
void build(int p,int l,int r){
	t[p].l=l;	t[p].r=r;
	if(l==r)	return ;	int mid=t[p].l+t[p].r>>1;
	build(p<<1,l,mid);	build(p<<1|1,mid+1,r);
}
void change(int p,int l,int r,int v){
	if(t[p].l==l&&t[p].r==r){
		t[p].sum+=(r-l+1)*v;	t[p].add+=v;	return ;
	}
	push_down(p);	int mid=t[p].l+t[p].r>>1;
	if(r<=mid)	change(p<<1,l,r,v);
	else if(l>mid)	change(p<<1|1,l,r,v);
	else	change(p<<1,l,mid,v),change(p<<1|1,mid+1,r,v);
	push_up(p);
}
int ask(int p,int l,int r){
	if(t[p].l==l&&t[p].r==r)	return t[p].sum;
	push_down(p);	int mid=t[p].l+t[p].r>>1;
	if(r<=mid)	return ask(p<<1,l,r);
	else if(l>mid)	return ask(p<<1|1,l,r);
	else	return ask(p<<1,l,mid)+ask(p<<1|1,mid+1,r);
}
int query(int x,int y){
	int res=0;
	while(bl[x]!=bl[y]){
		if(h[bl[x]]<h[bl[y]])	swap(x,y);
		res+=ask(1,pos[bl[x]],pos[x]);
		x=f[bl[x]];
	}
	if(pos[x]>pos[y])	swap(x,y);
	res+=ask(1,pos[x],pos[y]);
	return res;
}
signed main(){
	scanf("%lld %lld",&n,&m);
	for(int i=1;i<=n;i++)	scanf("%lld",&val[i]);
	for(int i=1;i<n;i++){
		int a,b;	scanf("%lld %lld",&a,&b);	add(a,b);	add(b,a);
	}
	dfs1(1);	dfs2(1,1);	build(1,1,n);
	for(int i=1;i<=n;i++)	change(1,pos[i],pos[i],val[i]);
	while(m--){
		int op,x,a;	scanf("%lld %lld",&op,&x);
		if(op==1)	scanf("%lld",&a),change(1,pos[x],pos[x],a);
		else if(op==2)	scanf("%lld",&a),change(1,pos[x],pos[x]+sz[x]-1,a);
		else	printf("%lld\n",query(1,x));
	}
	return 0;
}
全部评论

相关推荐

面试摇了我吧:啊哈哈面试提前五个小时发,点击不能参加就是放弃
点赞 评论 收藏
分享
10-15 09:13
已编辑
天津大学 soc前端设计
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务