关于树状数组:参看:http://128kj.iteye.com/blog/1743633
POJ3321 题意:
一棵具有n个节点的树,一开始,每个节点上都有一个苹果。现在给出m组动态的操作:
(C,i)是摘掉第i个节点上面的苹果(若苹果不存在,则为加上一个苹果),(Q,i)是查询以第i个节点为根的子树有几个苹果(包括第i个节点)。

输入是叉之间的关系,

1 2
1 3

就是主干上面两个叉分别是2 和3.


样例:
Sample Input

3
1 2
1 3
3
Q 1
C 2
Q 1

Sample Output

3
2



分析:
用树状数组求和,这个数组怎么来定?

从root开始dfs下去记录第一次访问u点时的时间戳为Begin[u](下标),当访问完以u为根的所有子树以后要向上回溯的时候,再记录一个时间戳End[u](下标),这样,这棵树上子树u上的所有苹果和就是Begin[u]到End[u]的区间和,操作就和普通的树状数组一样了~

下面是AC过的代码:

Java代码 复制代码 收藏代码
  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.io.StreamTokenizer;
  5. import java.util.*;
  6.  
  7. public class Main{
  8. //把苹果树作图处理了。
  9. private List<ArrayList<Integer>> G;// 邻接表。注:使用邻接矩阵内存溢出。
  10. private int k;//顶点数目
  11. private boolean[] visited;//判断顶点是否被访问过
  12. private int[] Begin;
  13. private int[] End;
  14. private int Count=0;
  15. private int Tree[];//树状数组
  16.  
  17.  
  18. public Main(int k,List<ArrayList<Integer>> G){
  19. this.k=k;
  20. this.G=G;
  21. visited = new boolean[k+1];
  22. Begin=new int[k+1];
  23. End=new int[k+1];
  24. Tree=new int[k+1];
  25. }
  26.  
  27. public int getBegin(int i){
  28. return Begin[i];
  29. }
  30.  
  31. private void cl(){
  32. for(int i=0;i<=k;i++)
  33. visited[i]=false;
  34. }
  35.  
  36. private int getEnd(int i){
  37. return End[i];
  38. }
  39.  
  40. public boolean getVis(int i){
  41. return visited[i];
  42. }
  43.  
  44. private void setVis(int i,boolean fal){
  45. visited[i]=fal;
  46. }
  47.  
  48.  
  49. private int lowbit(int i) {
  50. return i&(-i);
  51. }
  52.  
  53. void Update(int i,int Num){
  54. while(i<= k){
  55. Tree[i] += Num;
  56. i += lowbit(i);
  57. }
  58. }
  59.  
  60. int Sum(int i){
  61. int sum = 0;
  62. while(i>0){
  63. sum += Tree[i];
  64. i -= lowbit(i);
  65. }
  66. return sum;
  67. }
  68. public static void main(String[] args) throws IOException{
  69. StreamTokenizer sc = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
  70. int k;
  71. sc.nextToken();
  72. k = (int) sc.nval; //顶点数
  73.  
  74. List<ArrayList<Integer>> G;
  75. //构建邻接表
  76. G = new ArrayList<ArrayList<Integer>>();
  77. for(int i = 0;i<=k;i++)
  78. G.add(new ArrayList<Integer>());//初始化邻接表
  79. for (int i = 1; i <k; i++) {
  80. sc.nextToken();
  81. int u = (int) sc.nval;
  82. sc.nextToken();
  83. int v = (int) sc.nval;
  84. if(!G.get(u).contains(v)) {//避免重边的情况比如a b可能出现两次的情况
  85. G.get(u).add(v);
  86.  
  87. }
  88. //对于无向图
  89. if(!G.get(v).contains(u)) {
  90. G.get(v).add(u);
  91.  
  92. }
  93. }
  94.  
  95. Main ma=new Main(k,G);
  96. ma.dfs(1);
  97.  
  98. for(int i = 1;i <= k;i++){
  99. ma.Update(i,1);
  100. }
  101. sc.nextToken();
  102. int M=(int) sc.nval;
  103. ma.cl();
  104.  
  105. for(int i = 0;i < M;i++){
  106. sc.nextToken();
  107. String Op=sc.sval;
  108. sc.nextToken();
  109. int Num=(int) sc.nval;
  110. if(Op.equals("C")){
  111. if(!ma.getVis(Num))
  112. ma.Update(ma.getBegin(Num),-1);//摘苹果
  113. else
  114. ma.Update(ma.getBegin(Num),1);//长一个
  115. ma.setVis(Num,!ma.getVis(Num));
  116. }else if(Op.equals("Q")){
  117. System.out.printf("%d\n",ma.Sum(ma.getEnd(Num))-ma.Sum(ma.getBegin(Num)-1));
  118. }
  119. }
  120. }
  121.