苹果树 nkoj 2358/ poj 3321

【NOIP无压力模拟赛2】苹果树

Description

xth种了一棵苹果树,这棵树由n个节点构成,中间有树枝连接,苹果都会长在节点上,并且不会有两个苹果长在同一个节点上。Xth想知道某个子树上有多少个苹果,你能帮帮他吗?(1号节点为跟)

Input

第一行:一个整数n,表示苹果树有n个节点。 

以下n-1行:每行两个整数u、v,表示u、v两节点间有树枝相连。 

第n+1行:一个整数m,表示有m个询问或操作。 

以下m行:一个字符(’C’或’Q’)和一个整数x。‘C’表示将x节点处的苹果有无情况取反。‘Q’表示询问以x为根的子树中苹果的个数。 

注:苹果树开始时是长满苹果的。 

Output

对于每一个询问输出一行,一个整数,表示苹果数。

Sample Input

3 

1 2 

1 3 

3 

Q 1 

C 2 

Q 1 

Sample Output

3 

2 

Hint

N<=100000,M<=100000


分析:

题目给出的是一棵树,要求的是一颗子树(可视为区间)的和,还要求动态修改,这在树上不好实现。

一般多叉树有那些处理方法?①拓扑排序②深搜。拓扑排序在这里肯定是行不通。

题目中原来的编号顺序没有什么卵用,不妨从根开始深搜,按照探访的顺序编号,一颗子树必定对应着一段连续的区间
[L,R],L就是根节点。
现在要做的就是修改+查询区间和,当然用树状数组实现。
代码如下:
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#define LL long long
#define CLEAR(xxx) memset(xxx,0,sizeof(xxx))
using namespace std;
const int maxn=100000+5,inf=1e9;
int n,m,e,tot,L[maxn],R[maxn],s[maxn],c[maxn];
int to[maxn<<2],Next[maxn<<2],last[maxn];

inline int lowbit(int x) {return x&-x;}
inline void _read(int &x){
	char ch=getchar(); bool mark=false;
	for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true;
	for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
	if(mark)x=-x;
} 

void Add(int x,int d){
	for(;x<=n;x+=lowbit(x)) c[x]+=d;
}
int Sum(int x){
	int ans;
	for(ans=0;x>0;x-=lowbit(x)) ans+=c[x];
	return ans;
}

void add_edge(int From,int To){
	Next[++e]=last[From];
	last[From]=e;
	to[e]=To;
}

void DFS(int v,int fa){
	L[v]=++tot;   //开始探访v为根的子树 
	for(int i=last[v];i;i=Next[i]){
		if(to[i]==fa) continue;
		DFS(to[i],v);
	}
	R[v]=tot;      //v为根的子树探访完毕,记下结尾编号 
}

int main(){
	int i,x,y,p;
	_read(n); 
	for(i=1;i<n;i++){
		_read(x); _read(y);
		add_edge(x,y);
		add_edge(y,x);
	}
	DFS(1,0);
	for(i=1;i<=n;i++){    //开始挂满了苹果 
		s[i]=1;
		Add(i,1);
	}
	_read(m);
	while(m--){
		char ch;
		cin>>ch;
		_read(p);
		if(ch=='C'){
			p=L[p];
			if(s[p]==1)Add(p,-1);
			else Add(p,1);
			s[p]=1-s[p];
		}
		else {
			printf("%d\n",Sum(R[p])-Sum(L[p]-1));
		}
	}
	return 0;
}
/*
*/


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值