「HDU3966」 树上的询问 - 树链剖分

题目描述

给你一棵具有N个点(编号为1到N)M条边的树,并给定各个点权的值,然后有3种操作:

  1. I C1 C2 K:把C1与C2的路径上的所有点权值加上K
  2. D C1 C2 K:把C1与C2的路径上的所有点权值减去K
  3. Q C:查询节点编号为C的权值

分析

树剖模版题,只不过用树状数组就可以了,不必写线段树。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <stack>
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int N=50005;
struct Edge {
	int to,next;
}e[N*2];
int n,m,p;
int cnt,h[N],v[N];
int dep[N],prt[N],size[N],son[N];
int seg[N],top[N],sum[N];
void addedge(int x,int y) {
	e[++cnt]=(Edge){y,h[x]};
	h[x]=cnt;
}
void Dfs1(int x,int fa) {
	dep[x]=dep[fa]+1;
	size[x]=1;
	for (int i=h[x];i;i=e[i].next) {
		int y=e[i].to;
		if (y==fa) continue;
		prt[y]=x;
		Dfs1(y,x);
		size[x]+=size[y];
		if (size[son[x]]<size[y]) son[x]=y;
	}
}
void Dfs2(int x,int tp) {
	seg[x]=++seg[0];
	top[x]=tp;
	if (son[x]) Dfs2(son[x],top[x]);
	for (int i=h[x];i;i=e[i].next) {
		int y=e[i].to;
		if (top[y]) continue;
		Dfs2(y,y);
	}
}
int Ask(int x) {
	int ret=0;
	while (x) {
		ret+=sum[x];
		x-=lowbit(x);
	}
	return ret;
}
void update(int x,int y) {
	while (x<=seg[0]) {
		sum[x]+=y;
		x+=lowbit(x);
	}
}
void UpRange(int x,int y,int z) {
	int fx=top[x],fy=top[y];
	while (fx!=fy) {
		if (dep[fx]<dep[fy]) {
			swap(x,y);
			swap(fx,fy);
		}
		update(seg[fx],z);
		update(seg[x]+1,-z);
		x=prt[fx];
		fx=top[x];
	}
	if (dep[x]>dep[y]) swap(x,y);
	update(seg[x],z);
	update(seg[y]+1,-z);
}
int main() {
	while (~scanf("%d%d%d",&n,&m,&p)) {
		memset(sum,0,sizeof sum);
		memset(h,0,sizeof h);
		cnt=0;
		for (int i=1;i<=n;i++) scanf("%d",&v[i]);
		for (int i=1;i<=m;i++) {
			int a,b;
			scanf("%d%d",&a,&b);
			addedge(a,b);
			addedge(b,a);
		}
		Dfs1(1,1);
		Dfs2(1,1);
		for (int i=1;i<=n;i++) {
			update(seg[i],v[i]);
			update(seg[i]+1,-v[i]);
		}
		for (int i=1;i<=p;i++) {
			char ch[10];
			int x,y,z;
			scanf("%s%d",ch,&x);
			if (ch[0]=='I') {
				scanf("%d%d",&y,&z);
				UpRange(x,y,z);
			} else if (ch[0]=='D') {
				scanf("%d%d",&y,&z);
				UpRange(x,y,-z);
			} else {
				printf("%d\n",Ask(seg[x]));
			}
		}
	}
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值