图的深搜+树状数组练习 POJ 3321(JAVA)

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

输入是叉之间的关系,

1 2
1 3

就是主干上面两个叉分别是2 和3.
图的深搜+树状数组练习 POJ 3321(JAVA)

样例:
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过的代码:
import java.io.BufferedReader;   
import java.io.IOException;   
import java.io.InputStreamReader;   
import java.io.StreamTokenizer;   
import java.util.*; 

public class Main{
         //把苹果树作图处理了。
	private List<ArrayList<Integer>> G;// 邻接表。注:使用邻接矩阵内存溢出。 
	private int k;//顶点数目
	private boolean[] visited;//判断顶点是否被访问过
	private int[] Begin; 
        private int[] End;
        private int Count=0;
        private int Tree[];//树状数组
       

        public Main(int k,List<ArrayList<Integer>> G){
           this.k=k;
           this.G=G;
           visited = new boolean[k+1];
           Begin=new int[k+1];
           End=new int[k+1];
           Tree=new int[k+1];
       }
   
       public int getBegin(int i){
          return Begin[i];
       }
       
       private void cl(){
          for(int i=0;i<=k;i++)
            visited[i]=false;
       }

       private int getEnd(int i){
          return End[i];
       }
       
        public boolean getVis(int i){
          return visited[i];
        }

        private void setVis(int i,boolean fal){ 
               visited[i]=fal;
        }


    private int lowbit(int i) {
      return i&(-i);
   }

   void Update(int i,int Num){
    while(i<= k){
     Tree[i] += Num;
     i += lowbit(i);
   }
  }

 int Sum(int i){
    int sum = 0;
    while(i>0){
     sum += Tree[i];
     i -= lowbit(i);
    }
    return sum;
  }
 public static void main(String[] args) throws IOException{
	 StreamTokenizer sc = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));      
          int k;
          sc.nextToken();      
          k = (int) sc.nval;  //顶点数
  
         List<ArrayList<Integer>> G;  
         //构建邻接表
         G = new ArrayList<ArrayList<Integer>>();   
         for(int i = 0;i<=k;i++)   
            G.add(new ArrayList<Integer>());//初始化邻接表   
          for (int i = 1; i <k; i++) {
              sc.nextToken();  
            int u = (int) sc.nval;
               sc.nextToken();  
            int v = (int) sc.nval;
            if(!G.get(u).contains(v)) {//避免重边的情况比如a b可能出现两次的情况   
                G.get(u).add(v);  
                 
           }  
           //对于无向图 
           if(!G.get(v).contains(u)) {
                G.get(v).add(u);  
                 
           }   
	 }
 
         Main ma=new Main(k,G);
          ma.dfs(1);
    
         for(int i = 1;i <= k;i++){
           ma.Update(i,1);
         }
          sc.nextToken();  
          int M=(int) sc.nval;
          ma.cl();
         
        for(int i = 0;i < M;i++){
           sc.nextToken(); 
           String Op=sc.sval;
           sc.nextToken();  
           int Num=(int) sc.nval;
           if(Op.equals("C")){
              if(!ma.getVis(Num))
                ma.Update(ma.getBegin(Num),-1);//摘苹果
              else
                ma.Update(ma.getBegin(Num),1);//长一个
            ma.setVis(Num,!ma.getVis(Num));
          }else if(Op.equals("Q")){
             System.out.printf("%d\n",ma.Sum(ma.getEnd(Num))-ma.Sum(ma.getBegin(Num)-1));
          } 
       }      
    }
     

              
       
     // 深搜
  private void dfs(int v) {
     Begin[v] = ++Count;
      visited[v] = true;
     //System.out.print(v+" ");

   for (int i = 0; i <G.get(v).size(); i++) {   
        //递归调用搜索没有被访问过的当前节点的下一个节点(邻接点)   
     int k=G.get(v).get(i);
     if (!visited[k])   
          dfs(k);   
   }   
    End[v] = Count;
  }

}


源码:

你可能感兴趣的:(java,数据结构,编程,算法)