首页 > 代码库 > CF 383C Propagating tree [想法+树状数组]

CF 383C Propagating tree [想法+树状数组]

题意:

给一棵树

给出两种操作:

1.在某个结点上加上一个值,在这个结点所有的儿子结点上减去这个值,在这个结点的所有孙子结点上加上这个值,在所有曾孙子结点上减去这个值,直到底。

2.查询某个结点上的值

分析:

把这个问题转化为树状数组的区间求和

样例经过dfs处理后如下,每个结点处理出了两个值l,r层数1,2,3...,层数为奇数的属性为0,层数为偶数的属性为1

可以看到,子树下的结点的两个数值都是包含在子数的根结点的两数范围内的

而子树根结点的兄弟结点的两数范围则不是包含在子树根结点的两数范围内的


可以看到只有子树下面的结点和子树的根结点有联系,而兄弟结点没有联系,这和题目给的条件也是一致的

考虑维护两个树状数组

每当对一个结点进行加操作的时候,如果这个结点属性为0,则在0号树状数组上操作,否则在1号数组上操作

操作如下

在数组的l号元素加上该值,在数组r号元素上减去这个值


当查看一个结点的值的时候

这个结点要加上的值是属性号树状数组前l项和,要减去的是非属性号树状数组的前l项和,然后加上原值,输出


为什么这样做?

因为同属性的号的影响是加,不同属性号的影响是减

因为前l项和不会包含兄弟结点的影响,比如,这里要得到3号结点的值,l[3]=8,前8项和中,已经把2号上的一正(2号元素)一负(7号元素)给抵消了。而加上了父亲结点的,祖宗结点的影响,因为,3号结点的l值,在父亲结点祖宗结点的两数范围之间

#include <iostream>
#include <vector>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#define pb push_back
const int NN=222222;
int bit_2[2][NN*2+1];
int ALL_bit=NN*2;
int f[NN];
int clsfy[NN];
int sum_bit(int i,int bit[]){
	int s=0;while(i>0){s+=bit[i],i-= i & -i;}
	return s;
}
void add_bit(int i,int x,int bit[]){
	while(i<=ALL_bit){bit[i]+=x,i+= i & -i;}
}
vector<int> G[NN];
int m=1;
int l[NN],r[NN];
void dfs(int now,int fa,int k){
	l[now]=m++;
	clsfy[now]=k;
	for(int i=0;i<G[now].size();i++){
		int to=G[now][i];
		if(to==fa) continue;
		dfs(to,now,1-k);
	}
	r[now]=m++;
}
int main(){
#ifndef ONLINE_JUDGE
	freopen("/home/rainto96/in.txt","r",stdin);
#endif
	int n,m;cin>>n>>m;
	for(int i=1;i<=n;i++)	cin>>f[i];
	for(int i=1;i<=n-1;i++){
		int x,y;cin>>x>>y;
		G[x].pb(y);
		G[y].pb(x);
	}
	dfs(1,-1,0);
	while(m--){
		int kind;cin>>kind;
		if(kind==1){
			int pos,val;cin>>pos>>val;
			add_bit(l[pos],val,bit_2[clsfy[pos]]);
			add_bit(r[pos],-val,bit_2[clsfy[pos]]);
		}else{
			int pos;cin>>pos;
			cout<<f[pos]+sum_bit(l[pos],bit_2[clsfy[pos]])-sum_bit(l[pos],bit_2[1-clsfy[pos]])<<endl;
		}
	}
}


CF 383C Propagating tree [想法+树状数组]