CodeForces 384E Propagating tree 树状数组dfs序组合使用

Propagating tree

  CodeForces - 384E 
题意:有一棵树,上面每个点都有一个权值,这棵树有 两种操作:

1. 输入 1 x val 表示把第 x 个点权值加上一个 val,对于每个点,它权值改变了,val,则它的儿子权值改变-val

一直更新权值直到叶子节点

2.输入2 x ,表示查询 第 x 个点的权值

对于操作一,会发现,它是分层进行操作的,把层分为奇偶层,如果当前操作为奇数层+val,那么它下面的所有奇数层都是+val,偶数层都是-val。

先说说怎么把一棵树转化为线性来使用树状数组。 

由于对一个点进行操作的时候只需要对其子树也进行操作,所以这个线性关系就需要从父节点进入到儿子节点的,也就是一个先序遍历的关系,所以用一个dfs进去搜,然后给编号,对于每个点有两个编号,左编号就是自己的下标,右编号指的是自己最后的叶子节点的下标,所有 左编号到右编号就表示是从自己到自己的所有儿子都遍历到了,所以对这个点操作的时候就可以用那个编号在树状数组里进行维护了。

所以就进行一次 DFS,同时对层数奇偶进行赋值

void dfs(int x,int pre,int dep){
	tree[x].le = cnt++;
	tree[x].dep = dep;
	for(int i = 0;i < edge[x].size();i++){
		if(edge[x][i] == pre)
			continue;
		dfs(edge[x][i],x,1 - dep); 
	}
	tree[x].ri = cnt++;
}
然后再说怎样让它实现奇 偶层不同的赋值,答案是并不这么做,那太难实现了,我们不如分成两个树状数组,全部赋值为0,一个代表奇数层,一个代表偶数层。在进行1操作的时候,先判断它是奇数层还是偶数层,然后进去对应的层的树状数组里,在里面就不分奇偶地直接 + val 。我们对奇偶的区别在询问的时候再去做手脚。

我们已经做完了1操作,现在要进行查询操作的时候,假如它是奇数层的,那么它的价值应该要加上 奇数树状数组里的值,但是它得减去偶数树状数组里的值,因为偶数层操作的时候,它作为奇数层应该是要减的,所以这时候这样操作就可以了,也就是说我们先不把每个点的值放进树状数组,而是用数组数组记录该点的值的改变,最后来计算就可以了。

不可思议

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 200005
#define lowbit(x) (x & (-x))
#define mem(a,x) memset(a,x,sizeof(a))
struct node{
	int le,ri,val,dep; // le表示左标签,ri为右标签,dep记录层数奇偶
}tree[maxn];

int n,m,cnt,bit[2][maxn * 2];
vector<int>edge[maxn];

void Update(int x,int val,int *t){
	while(x <= 2 * n){
		t[x] += val;
		x += lowbit(x);
	}
}

int getSum(int x,int *t){
	int ans = 0;
	while(x > 0){
		ans += t[x];
		x -= lowbit(x);
	}
	return ans;
}

void dfs(int x,int pre,int dep){
	tree[x].le = cnt++;
	tree[x].dep = dep;
	for(int i = 0;i < edge[x].size();i++){
		if(edge[x][i] == pre)
			continue;
		dfs(edge[x][i],x,1 - dep); 
	}
	tree[x].ri = cnt++;
}

void init(){
	cnt = 1;
	mem(bit,0);
	for(int i = 0;i < maxn;i++)
		edge[i].clear();
}
int main(){
	init();
	scanf("%d %d",&n,&m);
	for(int i = 1;i <= n;i++){
		scanf("%d",&tree[i].val);
	}
	int a,b,x;
	for(int i = 1;i < n;i++){
		scanf("%d %d",&a,&b);
		edge[a].push_back(b);
		edge[b].push_back(a);
	}
	dfs(1,-1,0);
	for(int i = 0;i < m;i++){
		scanf("%d",&x);
		if(x == 1){
			scanf("%d %d",&a,&b);
			Update(tree[a].le,b,bit[tree[a].dep]); //上传的时候是从 该点一直刷到最后的,所以要反刷个 -b 把后面多刷的补回来 
			Update(tree[a].ri + 1,-b,bit[tree[a].dep]);
		}else{
			scanf("%d",&a);
			printf("%d\n",tree[a].val + getSum(tree[a].le,bit[tree[a].dep]) - getSum(tree[a].le,bit[1 - tree[a].dep]));
		} //由于做标签就是它的下标,所以可以直接拿tree[a].le 表示 
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值