C++题解:苹果树

  

题目 

  • 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 的状态需要更新(有苹果变为没有,没有苹果变为有)。

提示:由于数据量过大,建议大家使用scanfprintf接收和输出数据。

输出格式

对于每次询问输出一行,为查询的结果。

样例输入

3 2
1 2
1 3
C 2
Q 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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值