codeforces#225-C - Propagating tree-dfs序(奇偶)+线段树

http://codeforces.com/problemset/problem/383/C

题意:给一棵树,根为1,根高度为1,

每次操作 两种 

1:x,val,  把x节点加val,把其所有儿子-val,把儿子的儿子都+val,反复如此

2:查询某个节点的值



对每次操作,显然就是把该节点的所有子节点中,奇偶性与X相同的+val,不同的则-val,如此反复

我们先跑一遍dfs序,得到dfs序和每个节点深度,然后用dfs序建两颗线段树,一个是只保留深度为奇的节点,一颗只保留深度为偶的节点(其实直接建两个正常的树,只需要在查询的时候,奇数树上返回偶数深度节点值*0,奇数深度节点值*1即可,偶树同理)


那么每次操作,只需要 对 两个线段树 上下标为in【x】到out【X】的节点 分别 +val和 -val即可

最后每次查询。只需要在两棵树分别查询该节点值,最后返回他们的和




#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std; 
	const int N=200005;
int tm[200005],dp[N];
int who[200005+50];
struct tree
{
	int   add[4*N] ,vaild[4*N];
	void build(int l,int r,int i,int kind)    //  线段树的建立;  
	{  
		add[i]=0;      
		if(l==r) 
		{
			if (dp[who[l]]%2==kind)
			{add[i]=tm[who[l]]; vaild[i]=1; }
			return;
		}  
		int mid=(l+r)>>1;  
		build(l,mid,i<<1,kind);  
		build(mid+1,r,i<<1|1,kind);  
	}  
	void pushDown(int i, int l, int r)		//把i节点的延迟标记传递到左右儿子节点
	{
		if(add[i] != 0)
		{
			int mid = (l + r) >> 1;
			add[i << 1] += add[i]; 
			add[i << 1 | 1] += add[i]; 
			add[i] = 0;
		}
	} 
	void update(int i, int l, int r, int ql, int qr, int val)  
	{
		if(l > qr || ql > r) 
			return ;
		if(l >= ql && r <= qr)	 
		{	 
			add[i] += val;
			return ;
		}
		pushDown(i, l, r);		 
		int mid = (l + r) >> 1;
		update(i << 1, l, mid, ql, qr, val);
		update(i << 1 | 1, mid + 1, r, ql, qr, val);
 
	}
	
	int query(int i, int l, int r, int ql, int qr)	 
	{
		if(l > qr || ql > r)
			return 0;
		if(l == r && r == qr)
			return add[i]*vaild[i];
		pushDown(i, l, r);				//同update
		int mid =( l + r) >> 1;
		if (ql<=mid) return query(i << 1, l, mid, ql, qr) ;
        else return  query(i << 1 | 1, mid + 1, r, ql, qr);  
	}
};
int in[200005+50],out[200005+50],vis[200005+50];  
vector<int > mp[200005]; 
int id=0;
void dfs1(int x,int cur)
{
	in[x]=++id;
	vis[x]=1;
	dp[x]=cur;
	who[id]=x; //记下本id指向的节点
	int i;
	for (i=0;i<mp[x].size();i++)
	{
		int v=mp[x][i];
		if (!vis[v]) 	
			dfs1(v,cur+1);
	}
	out[x]=id;
}
tree odd,even;
int main()
{
    int n ,i,x,y,m;
	cin>>n>>m;
	for (i=1;i<=n;i++)
		scanf("%d",&tm[i]); 
	for (i=1;i<=n-1;i++)
	{
		scanf("%d%d",&x,&y);
		mp[x].push_back(y);
		mp[y].push_back(x);
	}
	dfs1(1,1);
	odd.build(1,n,1,1);
	even.build(1,n,1,0);
 
	int op,val;
	for (i=1;i<=m;i++)
	{
		scanf("%d",&op);
		if (op==1)
		{
			scanf("%d%d",&x,&val);
			if (dp[x]%2)
			{
			odd.update(1,1,n,in[x],out[x],val);
			even.update(1,1,n,in[x],out[x],-val);
			}
			else
			{
			even.update(1,1,n,in[x],out[x],val);
			odd.update(1,1,n,in[x],out[x],-val);
			}
		}
		else
		{
			scanf("%d",&x);
			printf("%d\n",odd.query(1,1,n,in[x],in[x])+even.query(1,1,n,in[x],in[x]));
		} 
		/*for (int j=1;j<=n;j++)
			printf("%d ",odd.query(1,1,n,in[j],in[j])+even.query(1,1,n,in[j],in[j]));
		printf("*******\n");*/
	}	 
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值