POJ_3321_Apple tree_dfs,树状数组

好想去骑车。

题意

一棵树,非二叉,点个数达到五次方,每个点可能有一个苹果或没有,初始都有,进行Q次操作
询问:问一棵子树上有多少苹果
修改:修改一个点上有无苹果的状态

IO

Input

The first line contains an integer N (N ≤ 100,000) , which is the number of the forks in the tree.
The following N - 1 lines each contain two integers u and v, which means fork u and fork v are connected by a branch.
The next line contains an integer M (M ≤ 100,000).
The following M lines each contain a message which is either
“C x” which means the existence of the apple on fork x has been changed. i.e. if there is an apple on the fork, then Kaka pick it; otherwise a new apple has grown on the empty fork.
or
“Q x” which means an inquiry for the number of apples in the sub-tree above the fork x, including the apple (if exists) on the fork x
Note the tree is full of apples at the beginning

Output
For every inquiry, output the correspond answer per line.

分析

用dfs先序遍历,对树上的点重新编号,并记录两种编号对应关系。
之所以好久没想出来怎么破,就是没想到先序遍历的性质:子树上根节点的编号总是最小,且子树上所有点的编号是连续的一段。
在这样的性质下,就可以在dfs时确定每个子树的编号连续段,也就是从子树根节点编号到一个最大值。所以每次查询一棵子树的苹果数量就变成了查询数组区间和,用树状数组和线段树都可。修改信息是单点修改,都很水。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define MXN 210000
int n,m;
int first[MXN],nxt[MXN],vv[MXN],count;
int idx[MXN],cnt;
int high[MXN];
int a[MXN],c[MXN];
bool flag[MXN];
int lowbit[MXN];
void init(){
    cnt=1;
    count=0;
    memset(flag,false,sizeof(flag));
    memset(first,-1,sizeof(first));
    memset(c,0,sizeof(c));
    for(int i=1;i<=n;++i){
        a[i]=1;
        for(int j=i;j<=n;j+=lowbit[j])
            c[j]+=1;
    }
}
void add(int u,int v){
    nxt[count]=first[u];
    first[u]=count;
    vv[count]=v;
    ++count;
}
void dfs(int now){
    flag[now]=true;
    idx[now]=cnt++;
    for(int i=first[now];i!=-1;i=nxt[i])    if(!flag[vv[i]])
        dfs(vv[i]);
    high[idx[now]]=cnt-1;
}
void update(int loc){
    int tem=1;
    if(a[loc])  tem=-1;
    a[loc]+=tem;
    for(int i=loc;i<=n;i+=lowbit[i])
        c[i]+=tem;
}
int find(int loc){
    int ret=0;
    for(int i=loc;i>0;i-=lowbit[i])
        ret+=c[i];
    return ret;
}
int main(){
    for(int i=0;i<MXN;++i)  lowbit[i]=i&(-i);
    while(scanf("%d",&n)!=EOF){
        init();
        for(int i=1;i<n;++i){
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);
            add(v,u);
        }
        dfs(1);
        scanf("%d",&m);
        for(int Q=0;Q<m;++Q){
            char ins[2];
            int tem;
            scanf("%s%d",ins,&tem);
            if(ins[0]=='Q')
                printf("%d\n",find(high[idx[tem]])-find(idx[tem]-1));
            else    update(idx[tem]);
        }
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值