码迷,mamicode.com
首页 > 其他好文 > 详细

[HAOI2015]树上操作

时间:2017-11-05 12:12:20      阅读:179      评论:0      收藏:0      [点我收藏+]

标签:c++   查询   区间查询   dfs序   return   cstring   set   题目   line   

题目:BZOJ4034、洛谷P3178。

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

操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。
现在要你输出所有询问的答案。
解题思路:对树上的节点修改查询,很容易想到树链剖分。
单点修改就相当于只包含一个节点的区间修改。
子树修改,由于树链剖分中一棵子树的dfs序连续,相当于区间修改。
查询就相当于多次区间查询。
树链剖分时间复杂度$O(n\log^2 n)$。
C++ Code:
#include<cstdio>
#include<cctype>
#include<cstring>
#define N 100005
#define mem(a) memset(&a,0,sizeof a)
#define ll long long
int n,m,a[N],aa[N],dfn[N],idx,cnt,head[N],sz[N],fa[N],top[N],son[N],dep[N];
ll ans;
struct edge{
	int to,nxt;
}e[N<<1];
struct SegmentTreeNode{
	ll s,add;
}d[N<<2];
inline int readint(){
	char c=getchar();
	bool b=false;
	for(;!isdigit(c);c=getchar())b=c==‘-‘;
	int d=0;
	for(;isdigit(c);c=getchar())
	d=(d<<3)+(d<<1)+(c^‘0‘);
	return b?-d:d;
}
void dfs(int now){
	sz[now]=1;
	for(int i=head[now];i;i=e[i].nxt)
	if(!dep[e[i].to]){
		dep[e[i].to]=dep[now]+1;
		fa[e[i].to]=now;
		dfs(e[i].to);
		sz[now]+=sz[e[i].to];
		if(!son[now]||sz[e[i].to]>sz[son[now]])
		son[now]=e[i].to;
	}
}
void dfs2(int now){
	dfn[now]=++idx;
	if(son[now])top[son[now]]=top[now],dfs2(son[now]);
	for(int i=head[now];i;i=e[i].nxt)
	if(dep[e[i].to]>dep[now]&&e[i].to!=son[now])
	top[e[i].to]=e[i].to,dfs2(e[i].to);
}
inline void pushdown(int len,int o){
	int l=o<<1,r=o<<1|1;
	d[l].add+=d[o].add;
	d[r].add+=d[o].add;
	d[l].s+=d[o].add*((len+1)>>1);
	d[r].s+=d[o].add*(len>>1);
	d[o].add=0;
}
void build(int l,int r,int o){
	if(l==r){
		d[o].s=aa[l];
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,o<<1);
	build(mid+1,r,o<<1|1);
	d[o].s=d[o<<1].s+d[o<<1|1].s;
}
void add(int l,int r,int o,int L,int R,int k){
	if(L<=l&&r<=R){
		d[o].add+=k;
		d[o].s+=(ll)k*(r-l+1);
		return;
	}
	pushdown(r-l+1,o);
	int mid=(l+r)>>1;
	if(L<=mid)add(l,mid,o<<1,L,R,k);
	if(mid<R)add(mid+1,r,o<<1|1,L,R,k);
	d[o].s=d[o<<1].s+d[o<<1|1].s;
}
void query(int l,int r,int o,int L,int R){
	if(L<=l&&r<=R){
		ans+=d[o].s;
		return;
	}
	pushdown(r-l+1,o);
	int mid=(l+r)>>1;
	if(L<=mid)query(l,mid,o<<1,L,R);
	if(mid<R)query(mid+1,r,o<<1|1,L,R);
}
void que(int x){
	while(top[x]!=1){
		query(1,n,1,dfn[top[x]],dfn[x]);
		x=fa[top[x]];
	}
	query(1,n,1,dfn[1],dfn[x]);
}
int main(){
	idx=cnt=0;
	n=readint(),m=readint();
	mem(dfn);mem(head);mem(sz);mem(fa);mem(top);mem(son);mem(dep);mem(d);
	for(int i=1;i<=n;++i)a[i]=readint();
	for(int i=1;i<n;++i){
		int u=readint(),v=readint();
		e[++cnt]=(edge){v,head[u]};
		head[u]=cnt;
		e[++cnt]=(edge){u,head[v]};
		head[v]=cnt;
	}
	fa[1]=top[1]=dep[1]=1;
	dfs(1);dfs2(1);
	for(int i=1;i<=n;++i)aa[dfn[i]]=a[i];
	build(1,n,1);
	while(m--){
		int f=readint();
		if(f==1){
			int x=readint(),k=readint();
			add(1,n,1,dfn[x],dfn[x],k);
		}else
		if(f==2){
			int x=readint(),k=readint();
			add(1,n,1,dfn[x],dfn[x]+sz[x]-1,k);
		}else{
			int x=readint();
			ans=0;
			que(x);
			printf("%lld\n",ans);
		}
	}
	return 0;
}

  

[HAOI2015]树上操作

标签:c++   查询   区间查询   dfs序   return   cstring   set   题目   line   

原文地址:http://www.cnblogs.com/Mrsrz/p/7786884.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!