Apple Tree
题意:给出一棵以1为根节点,有n个节点的树,每个节点存有 0 o r 1 0~or~1 0 or 1。有两种操作,(1) Q , x Q,x Q,x。(2) C , x C,x C,x。
(1).查询以第x个节点为根节点的子树,所有节点的和为多少。
(2).将第x个节点取反,0变1,1变0。
给出m个操作,并在树上完成些操作。
先修知识
d f s dfs dfs序
d f s 序 dfs序 dfs序,简单理解:通过 d f s dfs dfs遍历得到的一个序列。其实是两个序列,一个存的是遍历到第 i i i个节点的时刻,一个存的是遍历完第 i i i个节点的所有子节点的时刻
在代码中用 l [ N ] l[N] l[N]和 r [ N ] r[N] r[N],分别表示。(这也是个模板,先不考虑它的用途)
void dfs(int ne) {
l[ne] = tot;//记录遍历到第i个节点的时刻。
for(int i=head[ne]; i; i=edge[i].next) {
tot++;
dfs(edge[i].to);
}
r[ne] = tot;//记录遍历完所有子节点的时刻。
}
参考博客:DFS序——树链剖分前驱知识
树状数组
思路:现在先说一下dfs序在这题中有啥作用。
图 片 转 载 于 图片转载于 图片转载于POJ3321 Apple Tree (树状数组)
其中构成的 d f s dfs dfs序就是,形如 ( l [ i ] , r [ i ] ) {(l[i],r[i])} (l[i],r[i]),i从1到6分别为 [ 1 , 6 ] [ 2 , 4 ] [ 3 , 3 ] [ 4 , 4 ] [ 5 , 6 ] [ 6 , 6 ] [1,6] [2,4] [3,3] [4,4] [5,6] [6,6] [1,6][2,4][3,3][4,4][5,6][6,6], ( l [ i ] , r [ i ] ) {(l[i],r[i])} (l[i],r[i])第一个元素就是该节点所在位置,第二个元素是它能够管辖的最后一个元素下标(也就是以第i个节点为根的子树中最后的一个元素)。
这样一来。我们只要用树状数组维护每个节点中的值( 0 o r 1 0~or~1 0 or 1)。b = query(r[a]) - query(l[a]-1)
中第一个 q u e r y ( r [ a ] ) query(r[a]) query(r[a]),求的是以 1 1 1为根节点 r [ a ] r[a] r[a]为最后一个节点的树的节点总和,同理 q u e r y ( l [ a ] − 1 ) query(l[a]-1) query(l[a]−1)。用 q u e r y ( r [ a ] ) − q u e r y ( l [ a ] − 1 ) query(r[a])-query(l[a]-1) query(r[a])−query(l[a]−1)就是再节点x的管辖范围下节点值得和。
将思路概况一下,就是 d f s dfs dfs找 d f s dfs dfs序,用 d f s dfs dfs序的特点和树状数组求和。
代码:
// poj3233
#include<iostream>
#include<cstdio>
#include<vector>
#include<map>
#include<queue>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
const ll INF = 0x7ffffffffffff;
const int inf = 0x7ffffff;
int read() {
//快读模板。
int x = 0; int f = 1; char s = getchar();
while(s < '0' || s > '9') {
if(s == '-') f = -1; s = getchar();}
while(s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + s - 48; s = getchar();}
return x * f;
}
int head[N], vis[N], idx = 1;
int l[N], r[N], tot = 1, T[N], A[N];
struct E{
//链式前向星存树
int to, next;
}edge[2*N];
void add(int a, int b) {
edge[idx].to = b, edge[idx].next = head[a], head[a] = idx++;
}
void dfs(int ne) {
//普通dfs。
l[ne] = tot;//记录遍历到第i个节点的时刻。
for(int i=head[ne]; i; i=edge[i].next) {
tot++;
dfs(edge[i].to);
}
r[ne] = tot;//记录遍历完所有子节点的时刻。
}
//向树状数组中添加元素
void fills(int x, int val, int n) {
while(x <= n) {
T[x] += val;
x += x & (-x);
}
}
//询问节点总和。
int query(int x) {
int ans = 0;
while(x) {
ans += T[x];
x -= x & (-x);
}
return ans;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
int n, m, a, b;
char c;
while(scanf("%d", &n) != EOF) {
//初始化,特别重要
memset(edge, 0, sizeof edge);
memset(head, 0, sizeof head);
idx = 1, tot = 1;
memset(T, 0, sizeof T);
for(int i=1; i<n; i++) {
scanf("%d%d", &a, &b);
add(a, b);
}
dfs(1);//dfs找dfs序
for(int i=1; i<=n; i++) {
A[i] = 1;
fills(i, 1, n);
}
scanf("%d%*c", &m);//输入挺恶心的,注意%*c吃掉回车
for(int i=0; i<m; i++) {
scanf("%c %d%*c", &c, &a);//同上
if(c == 'C') {
if(A[a]) {
A[a] = 0;
fills(l[a], -1, n);
}
else {
A[a] = 1;
fills(l[a], 1, n);
}
}
else {
b = query(r[a]) - query(l[a]-1);
printf("%d\n", b);
}
}
}
return 0;
}