题目
- 1500ms
- 131072K
蒜头君的庄园里有一棵苹果树,树上有 N 个节点被 N−1 个树枝相连,蒜头君分别将它们标号为 1 到 N ,其中根为 1 号节点,每个节点开始的时候都有苹果,不过树上的苹果也是在变化的,有时某个节点会长出一个苹果,有时某个节点的苹果会被摘掉,蒜头君想知道某时刻某节点及其子树上一共有多少苹果,请你帮帮他。
输入格式
输入第一行两个整数 N,M ,表示一共有 N 个节点,M 次操作(节点状态更改或询问)(1≤N,M≤106)。
接下来 N - 1 行,每行两个数 u,v ,表示在这棵树上 u 是 v 的父节点。(1≤u,v≤N)
接下来 M 行,每行一个字符 x 和一个数 y ,如果 x 为
'Q'
,表示这一次是询问,询问节点 y 及其子树上一共有多少苹果,如果 x 为'C'
表示节点 y 的状态需要更新(有苹果变为没有,没有苹果变为有)。提示:由于数据量过大,建议大家使用
scanf
和printf
接收和输出数据。输出格式
对于每次询问输出一行,为查询的结果。
样例输入
3 21 21 3C 2Q 1样例输出
2
题解:
知识点:
树状数组
分析:
它问的是和,输入的又是树,易想到树状数组
然后呢?
我们可以对它输入的树用单链表表示,在dfs一下,利用时间戳给每个点编号。易得:编号后,一棵子树上的点的编号是连续的区间。这就可以用树状数组维护了,并解决了树状数组的更新了。
然后如何求和?【蓦淂,莞讹锝雏缇菍】我就败在这儿·······
我们要知道子树中的时间戳的最大值才可以。
核心:
void dfs(int u,int fa){
dfn1[u]=++times;
for (int i=h[u];~i;i=ne[i]){
if (e[i]==fa){
continue;
}
dfs(e[i],u);
}
dfn2[u]=times;//最大值
}
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=4e6+5,M=1e6+5;
int n,m;
int e[2*M],ne[2*M],h[M],idx;
int times,dfn1[M],dfn2[M];//dfn编号
bool vis[M];//标记该+1还是-1
int C[N];
inline void init(){
idx=0;
memset(h,-1,sizeof(h));
}
inline void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void dfs(int u,int fa){
dfn1[u]=++times;
for (int i=h[u];~i;i=ne[i]){
if (e[i]==fa){
continue;
}
dfs(e[i],u);
}
dfn2[u]=times;
}
inline int lowbit(int x){
return x&-x;
}
int getsum(int x){
int res=0;
for (int i=x;i>0;i-=lowbit(i)){
res+=C[i];
}
return res;
}
void update(int x,int v){
for (int i=x;i<=n;i+=lowbit(i)){
C[i]+=v;
}
}
int main(){
init();
scanf("%d%d",&n,&m);
for (int i=0;i<n-1;i++){
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
update(i+1,1);
}
update(n,1);
dfs(1,-1);
while (m--){
char c;
int x;
scanf(" %c%d",&c,&x);
if (c=='C'){
update(dfn1[x],vis[x]==true ? 1 : -1);
vis[x]^=1;
}else{
printf("%d\n",getsum(dfn2[x])-getsum(dfn1[x]-1));
}
}
return 0;
}