poj 3321 Apple Tree (dfs+树状数组)

题意:

有一颗苹果树,树的主干设为1,每一个分支有一个编号(1~n),代表这颗苹果树。每个分支上面只能最多有一个苹果,也就是一个枝子上面不可能有两个苹果,苹果树每个节点可以分出很多叉为多叉树。

输入是分支之间的关系,

1 2

1 3

就是主干上面两个叉分别是2 和3.

下面是两种操作,Q 和C

C   j  的意思是如果 j 这个枝子上面有苹果就摘下来,如果没有,那么就会长出新的一个

Q  j  就是问 j 这个叉上面的苹果总数。


从1开始按照dfs顺序给每个节点赋予编号(从1开始), 并保存下每个节点的子节点中最大的编号(确定该节点包含的区间范围)

然后用树状数组去求解

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 1e5+150;
int n;
struct edge{//
	int u, v;
}node[2*maxn];
int len=0, cnt=0;
int first[maxn], Next[maxn];
void addEdge(int u, int v){//数组模拟邻接表
	node[++len].u = u;
	node[len].v = v;
	Next[len] = first[u];
	first[u] = len ;
}
int s[maxn], e[maxn];
int c[maxn];
int vis[maxn];
//投影,记录每个节点包含的起点(本身投影的点)和终点(包含子节点中最大的投影点的编号)
void dfs(int pos){
	s[pos] = ++cnt;
	int k=first[pos];
	vis[pos] = 1;
	while(k!=-1){
		if(!vis[node[k].v]) dfs(node[k].v);
		k=Next[k];
	}
	e[pos] = cnt;
	return ;
}
int lowbit(int x){// 2^k
	return x&(-x);
}
void update(int pos, int val){//增加val
	while(pos<=cnt){
		c[pos] += val;
		pos += lowbit(pos);
	}
	return ;
}
int query(int pos){//查询区间和
	int sum=0;
	while(pos>0){
		sum += c[pos];
		pos -= lowbit(pos);
	}
	return sum;
}
int main()
{
	int i, m;
	//freopen("in.txt", "r", stdin);
	while(~scanf("%d", &n)){
		len=0;
		cnt=0;
		memset(vis, 0, sizeof(vis));
		memset(first, -1, sizeof(first));
		memset(c, 0, sizeof(c));
		int u, v;
		for(i=1; i<n; i++){
			scanf("%d %d", &u, &v);
			addEdge(u, v);
		}
		dfs(1);
		// for(i=1; i<=n; i++){
		// 	printf("%d - %d\n", s[i], e[i]);
		// }
		for(i=1; i<=cnt; i++){
			update(i, 1);
			vis[i]=1;
		}
		scanf("%d", &m);
		char op;
		int pos;
		while(m--){
			scanf("%*c%c %d", &op, &pos);
			if(op=='C'){
				if(vis[pos]){
					update(s[pos], -1);
				} else {
					update(s[pos], 1);
				}
				vis[pos] = !vis[pos];
			} else {
				printf("%d\n", query(e[pos])-query(s[pos])+vis[pos]);
			}
		}
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值