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

4034: [HAOI2015]树上操作

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 7160  Solved: 2435
[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

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

思路:

dfs序维护一个子树的区间,求节点到根的和时用树链剖分处理。

代码:


#include<bits/stdc++.h>
using namespace std;
#define ll long long 
const int maxn=1e5+10;
int n,Q,tol,a[maxn];
int seg[maxn],rev[maxn],size[maxn],son[maxn],dep[maxn];
int fa[maxn],top[maxn],LL[maxn],RR[maxn];
int head[maxn];
ll sum[maxn<<2],add[maxn<<2];
struct node
{
	int to,next;
}rode[maxn*2];
void add_edge(int a,int b)
{
	rode[tol].to=b;
	rode[tol].next=head[a];
	head[a]=tol++;
}
void dfs1(int v,int father)
{
	size[v]=1;
	dep[v]=dep[father]+1;
	fa[v]=father;
	for(int i=head[v];i!=-1;i=rode[i].next)
	{
		node e=rode[i];
		if(e.to==father) continue;
		dfs1(e.to,v);
		size[v]+=size[e.to];
		if(size[son[v]]<size[e.to]) son[v]=e.to;
	}
}
void dfs2(int v,int father)
{
	LL[v]=seg[0];
	if(son[v])
	{
		seg[son[v]]=++seg[0];
		rev[seg[0]]=son[v];
		top[son[v]]=top[v];
		dfs2(son[v],v);
	}
	for(int i=head[v];i!=-1;i=rode[i].next)
	{
		node e=rode[i];
		if(!top[e.to])
		{
			seg[e.to]=++seg[0];
			rev[seg[0]]=e.to;
			top[e.to]=e.to;
			dfs2(e.to,v);
		}
	}
	RR[v]=seg[0];
}
void build(int l,int r,int rt)
{
	add[rt]=0;
	if(l==r)
	{
		sum[rt]=a[rev[l]];
		return;
	}
	int mid=(l+r)/2;
	build(l,mid,rt<<1);
	build(mid+1,r,rt<<1|1);
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void push_down(int l,int r,int rt)
{
	if(add[rt])
	{
		int mid=(l+r)/2;
		sum[rt<<1]+=add[rt]*(mid-l+1);
		sum[rt<<1|1]+=add[rt]*(r-mid);
		add[rt<<1]+=add[rt];
		add[rt<<1|1]+=add[rt];
		add[rt]=0;
	}
}
void update(int L,int R,int C,int l,int r,int rt)
{
	if(l>=L&&r<=R)
	{
		add[rt]+=C;
		sum[rt]=sum[rt]+1LL*C*1LL*(r-l+1);
		return;
	}
	push_down(l,r,rt);
	int mid=(l+r)/2;
	if(L<=mid) update(L,R,C,l,mid,rt<<1);
	if(R>mid) update(L,R,C,mid+1,r,rt<<1|1);
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
ll query(int L,int R,int l,int r,int rt)
{
	if(l>=L&&r<=R) return sum[rt];
	push_down(l,r,rt);
	int mid=(l+r)/2;
	ll ans=0;
	if(L<=mid) ans+=query(L,R,l,mid,rt<<1);
	if(R>mid) ans+=query(L,R,mid+1,r,rt<<1|1);
	return ans;
}
ll work(int x)
{
	ll ans=0;
	int fx=top[x];
	while(fx!=1)
	{
		ans+=query(seg[fx],seg[x],1,seg[0],1);
		x=fa[fx];fx=top[x];
	}
	return ans+query(seg[fx],seg[x],1,seg[0],1);
}
int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&Q);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<n;i++)
	{
		int x,y;scanf("%d%d",&x,&y);
		add_edge(x,y);add_edge(y,x);
	}
	dfs1(1,0);
	seg[0]=seg[1]=rev[1]=top[1]=LL[1]=1;
	dfs2(1,0);
	build(1,n,1);
	while(Q--)
	{
		int op,x,y; scanf("%d",&op);
		if(op==1)
		{
			scanf("%d%d",&x,&y);
			update(seg[x],seg[x],y,1,seg[0],1);
		}
		else if(op==2)
		{
			scanf("%d%d",&x,&y);
			update(LL[x],RR[x],y,1,seg[0],1);
		}
		else
		{
			scanf("%d",&x);
			printf("%lld\n",work(x));
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/albertluf/article/details/81253286