zoukankan      html  css  js  c++  java
  • NYOJ 231 Apple Tree (树状数组)

    题目链接
    描述
    There is an apple tree outside of kaka's house. Every autumn, a lot of apples will grow in the tree. Kaka likes apple very much, so he has been carefully nurturing the big apple tree.

    The tree has N forks which are connected by branches. Kaka numbers the forks by 1 to N and the root is always numbered by 1. Apples will grow on the forks and two apple won't grow on the same fork. kaka wants to know how many apples are there in a sub-tree, for his study of the produce ability of the apple tree.

    The trouble is that a new apple may grow on an empty fork some time and kaka may pick an apple from the tree for his dessert. Can you help kaka?

    输入
    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
    输出
    For every inquiry, output the correspond answer per line.
    样例输入
    3
    1 2
    1 3
    3
    Q 1
    C 2
    Q 1
    样例输出
    3
    2

    分析:

    有一棵苹果树,刚开始的时候每个节点上都有且仅有一颗苹果,需要进行一系列操作,Q N表示的是要查询N节点下有多少个苹果(包括N节点本身和它下面所有的子节点),C N表示的是如果N节点上有苹果的话,就把这个苹果拿走,否则就往这个节点上放一个苹果。

    在查询的时候,要求出这个节点下的所有的苹果树,相当于求的是一个区间和,如果给这棵树的每个节点都给合理的编号,那么就可以按照树状数组来求区间和了。

    Step 1:

    如下图,可以看到,由于普通的树并不具有区间的性质,所以在考虑使用线段树作为解题思路时,需要对给给定的数据进行转化,首先对这棵树进行一次dfs遍历,记录dfs序下每个点访问起始时间与结束时间,记录起始时间是前序遍历,结束时间是后序遍历,同时对这课树进行重标号。

    Step 2:

         如下图,DFS之后,那么树的每个节点就具有了区间的性质。
    
    
    
         那么此时,每个节点对应了一个区间,而且可以看到,每个节点对应的区间正好“管辖”了它子树所有节点的区间,那么对点或子树的操作就转化为了对区间的操作。
    

    Step 3:

         这个时候,每次对节点进行更新或者查询,就是线段树和树状数组最基本的实现了…
    

    代码:

    include<stdio.h>

    include

    include

    using namespace std;
    vectorv[100005];
    int le[100005];
    int ri[100005];
    int bj[100005];
    int e[100005];
    int Apple[100005];
    int cnt,n;

    void dfs(int id)///相当于给树中的每一个节点找到合适的区间
    {
    int op;
    le[id]=++cnt;
    bj[id]=1;
    for(int i=0;i<v[id].size();i++)
    {
    op=v[id][i];
    if(bj[op]0)
    {
    dfs(op);
    }
    }
    ri[id]=cnt;
    }

    int lowBit(int n)
    {
    return n&(-n);
    }

    int sum(int n)///求和
    {
    int num=0;
    while(n>0)
    {
    num+=e[n];
    n=n-lowBit(n);
    }
    return num;
    }

    void Updata(int num,int a)///更新
    {
    while(num<=n)
    {
    e[num]+=a;
    num+=lowBit(num);
    }
    }
    int main()
    {
    int a,b,m;
    char ch;
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
    scanf("%d%d",&a,&b);
    v[a].push_back(b);
    v[b].push_back(a);
    }
    cnt=0;
    dfs(1);
    for(int i=1;i<=n;i++)
    {
    Apple[i]=1;///刚开始的时候每个节点都有一个苹果
    Updata(i,1);///初始化的时候树中的每个节点都要更新
    }
    scanf("%d",&m);
    while(m--)
    {
    scanf(" %c%d",&ch,&a);
    if(ch
    'Q')
    {
    printf("%d ",sum(ri[a])-sum(le[a]-1));
    }
    if(ch'C')
    {
    if(Apple[a]
    1)
    {
    Apple[a]=0;
    Updata(le[a],-1);///节点上的苹果去掉,涉及它的每个区间上都要减掉一个
    }
    else
    {
    Apple[a]=1;
    Updata(le[a],1);///节点上添加一个新的苹果,涉及它的每个区间上也要加上一个苹果
    }
    }
    }
    return 0;
    }

  • 相关阅读:
    Scanner类
    BufferedReader类
    打印类
    管道流
    内存操作流
    转换流——OutputStreamWriter类与InputStreamReader类
    Java字节流与字符流基本操作
    RandomAccessFile类
    File类
    Timer类和TimerTask类
  • 原文地址:https://www.cnblogs.com/cmmdc/p/6762575.html
Copyright © 2011-2022 走看看