poj 3321 Apple Tree(树状数组)

题意:一棵具有n个节点的树,一开始,每个节点上都有一个苹果。现在给出m组动态的操作:(C,i)是摘掉第i个节点上面的苹果(若苹果不存在,则为加上一个苹果),(Q,i)是查询以第i个节点为根的子树有几个苹果(包括第i个节点)。

 

思路:

树状数组。这道题重点怎么建立树到树状数组的映射关系:利用dfs遍历树,对每个节点进行两次编号,第一次搜到第i个节点时的深度dep,为这个节点管辖区间的上限low[i](也为这个节点的下标),然后搜这个节点的子节点,最后搜回来后的深度dep,为这个节点管辖区间的下限high[i],如下图所示。接下来就是树状数组部分了。

poj <wbr><wbr>3321 <wbr><wbr>: <wbr><wbr>Apple <wbr><wbr>Tree <wbr><wbr>(树状数组)

 

//4860K      454MS

#include <stdio.h>
#include <string.h>
#define M 100010
#define lowbit(x) (x&(-x))
int head[M],low[M],high[M],vis[M],mark[M];
int ar[M];
int n,p,dep;
struct data
{
      int to,next;
}edge[M];

void dfs (int u)                    //这个映射方法太巧妙了,学习了
{
      low[u] = ++dep;            //第一次的深度
      vis[u] = 1;
      for (int i = head[u];i != -1;i = edge[i].next)
              while (!vis[edge[i].to])
                      dfs (edge[i].to);
      high[u] = dep;            //返回的深度
}
void addedge (int cu,int cv)
{
      edge[p].to = cv;
      edge[p].next = head[cu];
      head[cu] = p ++;
}

void add (int u,int w)
{
      while (u <= n)
      {
              ar[u] += w;
              u += lowbit(u);
      }
}

int sum (int u)
{
      int ans = 0;
      while (u > 0)
      {
              ans += ar[u];
              u -= lowbit(u);
      }
      return ans;
}
int main ()
{
      int i,m,u,v,root;
      char op;
      while (~scanf ("%d",&n))
      {
              p = 0;
              dep = 0;
              memset (head,-1,sizeof(head));
              memset (ar,0,sizeof(ar));
              memset (vis,0,sizeof(vis));

              for (i = 1;i <= n;i ++)
              {
                      mark[i] = 1;              //1表示有苹果
                      add(i,1);
              }
              for (i = 1;i < n;i ++)
              {
                      scanf ("%d %d",&u,&v);
                      addedge(u,v);
              }
              dfs (1);
              scanf ("%d",&m);
              while (m --)
              {
                      getchar ();
                      scanf ("%c %d",&op,&root);
                      if (op == 'Q')
                              printf ("%d\n",sum(high[root])-sum(low[root] -1));
                      else
                      {
                              if (mark[root])
                              {
                                      add (low[root],-1);
                                      mark[root] = 0;
                              }
                              else
                              {
                                      add (low[root],1);
                                      mark[root] = 1;
                              }
                      }
              }
      }
      return 0;
}


     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值