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;
}