PKU3321 Apple Tree - 树状数组

题目描述:

苹果树上有N个节点,苹果只能长在节点上,一个节点要么有一个苹果要么没有。给出树的结构,一种操作是将某个节点的苹果栽下或长出苹果;另一种操作是询问某个节点为根的子树总共有多少苹果。

分析:

题目变形很厉害,不过任然可以用树状数组来解决。

我们将节点重新编号,使得所有孩子节点的编号都小于父亲节点的编号,那么当询问子树总共有多少苹果时就是询问比该节点编号小的节点有多少苹果。也就是树状数组的模型了。

编号规则:照后续遍历的方式给节点编号,得到的编号一定是子树中最大的max,并且记录下该节点之下的最小编号min。那么转换为树状数组模型后,询问节点v时,则回答Sum(v.max)-Sum(v.min-1)。

当然,此题也可以编号之后用线段树来解决。

 

  1. /*
  2. PKU3321 Apple Tree
  3. */
  4. #include <stdio.h>
  5. #include <memory.h>
  6. #include <malloc.h>
  7. #define clr(a) memset(a,0,sizeof(a))
  8. #define N 100005
  9. typedef struct LinkNode{
  10.     int id;
  11.     struct LinkNode* next;
  12. }Link;
  13. typedef struct{
  14.     int index;
  15.     int min;
  16. }Index;
  17. //vars
  18. Link mem[N*2];
  19. int pmem;
  20. int n,m;
  21. Link* a[N];
  22. int e[N];
  23. Index idx[N];
  24. int nidx;
  25. int b[N];
  26. int c[N];
  27. void addNode(int i,int j){
  28.     Link *p = &mem[pmem++];
  29.     p->id=j;
  30.     p->next=a[i];
  31.     a[i]=p;
  32. }
  33. void Traversal(int v){
  34.     Link *p;
  35.     e[v]=1;
  36.     idx[v].min=nidx;
  37.     
  38.     p=a[v];
  39.     while(p!=NULL){
  40.         if(!e[p->id]){
  41.             Traversal(p->id);
  42.         }
  43.         p=p->next;
  44.     }
  45.     idx[v].index=nidx++;
  46. }
  47. int lowbit(int x){
  48.     return -x&x;
  49. }
  50. void Add(int i,int t){
  51.     while(i<=n){
  52.         c[i]+=t;
  53.         i+=lowbit(i);
  54.     }
  55. }
  56. int Sum(int i){
  57.     int s=0;
  58.     while(i>0){
  59.         s+=c[i];
  60.         i-=lowbit(i);
  61.     }
  62.     return s;
  63. }
  64. int main()
  65. {
  66.     while(scanf("%d",&n)!=EOF){
  67.         int i,j,k;
  68.         //input
  69.         clr(a); pmem=0;
  70.         
  71.         for(k=0;k<n-1;k++){
  72.             scanf("%d%d",&i,&j);
  73.             addNode(i,j);
  74.             addNode(j,i);
  75.         }
  76.         
  77.         //search tree
  78.         clr(e);
  79.         nidx=1;
  80.         Traversal(1);
  81.         
  82.         //init tree
  83.         for(k=1;k<=n;k++){
  84.             b[k]=1;
  85.             Add(k,1);
  86.         }
  87.         
  88.         //work
  89.         char ch[5];
  90.         int sum;
  91.         scanf("%d",&m);
  92.         while(m--){
  93.             scanf("%s%d",ch,&k);
  94.             i=idx[k].index;
  95.             j=idx[k].min;
  96.             
  97.             if(ch[0]=='C'){  //change
  98.                 b[i]=!b[i];
  99.                 Add(i,b[i]?1:-1);
  100.             }
  101.             if(ch[0]=='Q'){  //inquiry
  102.                 sum=Sum(i)-Sum(j-1);
  103.                 printf("%d/n",sum);
  104.             }
  105.         }
  106.     }
  107.     
  108.     return 0;
  109. }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值