【原】 POJ 3321 Apple Tree 深度优先生成树+树状数组 解题报告

 

http://poj.org/problem?id=3321


方法:
该题要经常查询数值总是变化的区间和。
如果采取常规做法求得积累数组c[N],那么当a[i]改变时,同时需要改变c[i...N],使得每次调整都会很慢,复杂度O(N)。求和复杂度O(1)
树状数组BIT形状很像二项树,适用于对经常改变的数组快速求得区间和。
采用树状数组tree[N]的话,每次调整与求和的复杂度为O(logN),效率大大提高。

 

介绍BIT的好文
http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=binaryIndexedTrees 

http://blog.sina.com.cn/s/blog_49c5866c0100f4l7.html



难点:
因此需要将题中给的无向图转化为数组从而易于求得区间和。
利用DFS将无向图编号为树,同时由此编号做索引可以转化为数组来处理。
DFS求得的树的特点:以某节点i为根的子树中,所有节点的编号相连,且i为最小。这样形成的树便于求得某子树(连续子数组)的区间和。

 

注意:
邻接表不要用vector,会超时;也不要用int[N][N],那样会MLE。自己手写数组+链表来做

 

Description

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?

Input

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

Output

For every inquiry, output the correspond answer per line.

Sample Input

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

Sample Output

3
2

 

 

   1: #include <stdio.h>
   2:  
   3: const int N = 100001 ;
   4:  
   5: //DFS树中某节点的子树中最小和最大的节点编号,其中最小编号即为该节点的dfs编号
   6: struct dfsRge
   7: {
   8:     int b ; int e ;
   9: };
  10:  
  11: //用数组加链表实现邻接表
  12: struct Node
  13: {
  14:     Node(int v=0,Node *p=0){vertex=v;next=p;} //***
  15:     int vertex ;
  16:     Node *next ;
  17: };
  18:  
  19: Node G[N] ;      //邻接表,不要写成Node *G[N]
  20: int tree[N] ;    //BIT
  21: char apple[N] ;  //BIT对应的数组
  22:  
  23: dfsRge dfsNo[N] ; //dfsNo[u].a=i ; dfsNo[u].b=j
  24:                   //表示原始图中编号为u的节点,其在DFS生成树中的编号为i,该节点
  25:                   //子树中节点的最小编号为i,最大编号为j
  26: bool visited[N] = {false} ;  //DFS标记节点是否访问过的数组
  27:  
  28:  
  29: //==========================Binary Indexed Tree================================
  30:  
  31:  
  32: // 2^r = 1<<r = ( idx & -idx )
  33: // r is a position in idx of the last digit 1 (from left to right) in binary notation.
  34: // 该运算利用了补码的性质,只保留idx二进制表示中的最后一位1,其余位全置0
  35:  
  36:  
  37: //参数为节点数
  38: //因为初始数组所有元素都为1,所以BIT各索引的值即为各索引对应数组元素的个数2^r
  39: //tree[idx] is sum of value from array a's index (idx - 2^r + 1) to index idx .
  40: void BuildBIT( int n )
  41: {
  42:     int i ;
  43:  
  44:     for( i=1 ; i<=n ; ++i )
  45:         tree[i] = ( i & -i ) ;
  46: }
  47:  
  48: //得到BIT对应的数组a中的前缀和:c[idx]=a[1]+a[2]+...+a[idx]
  49: //Eg. In binary notation, 13 is equal to 1101.
  50: //c[1101] = tree[1101] + tree[1100] + tree[1000] 
  51: //        = ( a[13] ) + ( a[9]+...+a[12] ) + ( a[1]+...+a[8] )
  52: //复杂度:O(logn)
  53: int GetSum( int idx )
  54: {
  55:     int sum = 0 ;
  56:     while( idx>0 )
  57:     {
  58:         sum += tree[idx] ;
  59:         idx -= ( idx & -idx ) ;  //得到idx之前的不相交集合的索引
  60:     }
  61:     return sum ;
  62: }
  63:  
  64: //n为最大索引值
  65: //使得a[idx]+=val,因此需要更新tree[idx...n]中所有包含a[idx]的元素,即需要更新tree中
  66: //tree[idx]的所有父(祖父...)节点
  67: //复杂度:O(logn)
  68: void AddVal( int n , int idx , int val )
  69: {
  70:     while( idx<=n )
  71:     {
  72:         tree[idx] += val ;
  73:         idx += ( idx & -idx ) ;  //得到idx的父节点
  74:     }
  75: }
  76:  
  77: //=============================================================================
  78:  
  79: //得到邻接表
  80: //返回节点数n
  81: int ReadGraph()
  82: {
  83:     int n ;
  84:     int u,v ;
  85:     int idx ;
  86:     int i ;
  87:  
  88:     scanf( "%d", &n ) ;
  89:  
  90:     for( i=1 ; i<n ; ++i )  //n-1条边
  91:     {
  92:         scanf( "%d%d", &u,&v ) ;
  93:         G[u].next = new Node(v,G[u].next) ;  //在头结点插入
  94:         G[v].next = new Node(u,G[v].next) ;        
  95:     }
  96:  
  97:     return n ;
  98: }
  99:  
 100: //得到深度优先生成树,便于求区间和
 101: //将每棵子树限定在区间(dfsNo[v].b,dfsNo[v].e)里面
 102: //Dfs(1,1)  从原始图节点1开始,对图的初始编号为1
 103: void Dfs( int v, int& num )
 104: {
 105:     visited[v] = true ;
 106:     dfsNo[v].b = num ;  //该节点在DFS生成树中的编号
 107:                         //也是该节点子树中节点的最小编号
 108:     for( Node* p=G[v].next ; p!=0 ; p=p->next )
 109:     {
 110:         int adjV = p->vertex ;
 111:         if( !visited[adjV] )
 112:             Dfs( adjV , ++num );
 113:     }
 114:     dfsNo[v].e = num ;  //该节点子树中节点的最大编号
 115: }
 116:  
 117: void run3321()
 118: {
 119:     int i ;
 120:     int n,m ;
 121:     char q ;
 122:     int x,dfs,chgVal ;
 123:     int cnt ;
 124:  
 125:     n = ReadGraph() ;
 126:  
 127:     //初始时数组元素全为1
 128:     for( i=0 ; i<=n ; ++i )
 129:             apple[i] = 1 ;
 130:  
 131:     i = 1 ;
 132:     Dfs( 1, i ) ;
 133:     BuildBIT( n ) ;
 134:  
 135:     scanf( "%d", &m );
 136:     while( m-- )
 137:     {
 138:         scanf( "\n%c %d", &q,&x ) ;  //scanf( "%c %d", &q,&x )会出错,因为它将回车读入q
 139:         if( q=='C' ) 
 140:         {
 141:             dfs = dfsNo[x].b ;       //求得原始图中编号为x的节点在树中的编号(数组的索引)
 142:             chgVal = apple[dfs]==1 ? -1 : 1 ;
 143:             apple[dfs] ^= 1 ;        //利用异或1,将1-->0,0-->1
 144:             AddVal( n , dfs , chgVal ) ;  //更新BIT
 145:         }
 146:         else  // q=='Q'
 147:             printf( "%d\n", GetSum(dfsNo[x].e)-GetSum(dfsNo[x].b-1) ) ; //求区间和
 148:     }
 149: }

posted @ 2010-11-08 19:50  Allen Sun  阅读(506)  评论(0编辑  收藏  举报