POJ 3321 Apple Tree 【樹狀數組+建樹】
題目鏈接:http://poj.org/problem?id=3321
Apple Tree
Time Limit: 2000MS |
|
Memory Limit: 65536K |
Total Submissions: 34812 |
|
Accepted: 10469 |
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
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
Source
POJ Monthly--2007.08.05, Huang, Jinsong
題意概括:
給一個 N 階無向圖,要求把這個無向圖轉換為一顆以 1 為根的樹後進行兩種操作:
C x :x結點上如果有蘋果就摘掉,如果沒有蘋果就長出一個新的來
Q x: 查詢 x 結點與它的所有後代分支一共有幾個蘋果
解題思路:
因為原本蘋果之間的關系我們不好處理,所以就給他們這些結點重新編號。
並且為了方便子樹的蘋果數求和(即區間查詢)我們在給結點增加一個左值一個右值表示當前結點管轄的範圍。
如何建樹?
根據題目一開始給的邊與邊的關系我們可以通過 dfs 進行建樹。
L[ x ] (X的左值): X結點的新編號就是該結點的左值,表示它管轄範圍的下限
R[ x ] (X的右值):而子樹的編號最大值為該結點的右值,表示它管轄範圍的上限。
至於dfs順序,如下
舉個栗子:
N = 6;
1 - 2
1 - 6
2 - 5
2 - 3
6 - 4
到這裏感覺有一點點線段樹的味道了。。。
但這裏要用樹狀數組對這些區間進行維護和查詢。
例如:我要 Q 2;
由上面建好的新樹我們可以知道 結點2這顆子樹包含了結點2、結點5、結點3,而結點2所管轄的區間【2,4】剛好把它們包括進去了。
如果我們要求 這可子樹的蘋果數量,其實就是用樹狀數組求區間【2,4】的蘋果總數
也就是右值的前綴和減去左值的前綴和(但不包括左值,因為結點本身也要算進去)即:ans = sum( R[ 2 ] ) - sum( L[ 2 ] - 1);
而C操作其實就是樹狀數組的單點更新而已。
註意事項:
一開始用 stl 的 vector 存無向圖,雖然過了但效率不咋地。想想好久沒用靜態鄰接表了,換了一下,快了好多。
AC code:
1 ///建樹+樹狀數組(stl)8080k 1047ms 2 /* 3 #include <cstdio> 4 #include <iostream> 5 #include <cstring> 6 #include <vector> 7 #include <algorithm> 8 #define INF 0x3f3f3f3f 9 using namespace std; 10 typedef vector<int>ve; 11 12 const int MAXN = 1e5+10; 13 int t[MAXN], l[MAXN], r[MAXN]; 14 bool vis[MAXN]; 15 int N, M, cnt; 16 17 vector<ve>node(MAXN); 18 19 int lowbit(int x) 20 { 21 return x&(-x); 22 } 23 void add(int x, int value) 24 { 25 for(int i = x; i <= N; i+=lowbit(i)) 26 t[i]+=value; 27 } 28 int sum(int x) 29 { 30 int res = 0; 31 for(int i = x; i > 0; i-=lowbit(i)) 32 res+=t[i]; 33 return res; 34 } 35 void dfs(int k) 36 { 37 l[k] = cnt; 38 for(int i = 0; i < node[k].size(); i++) 39 { 40 cnt++; 41 dfs(node[k][i]); 42 } 43 r[k] = cnt; 44 return; 45 } 46 int main() 47 { 48 scanf("%d", &N); 49 int a, b; 50 for(int i = 1; i <= N; i++) 51 { 52 vis[i] = 1; 53 add(i, 1); 54 } 55 for(int i = 1; i < N; i++) 56 { 57 scanf("%d%d", &a, &b); 58 node[a].push_back(b); 59 } 60 cnt = 1; 61 dfs(1); 62 scanf("%d", &M); 63 while(M--) 64 { 65 char com[3]; 66 int px; 67 scanf("%s", &com); 68 if(com[0] == ‘C‘) 69 { 70 scanf("%d", &px); 71 if(vis[px]) add(l[px], -1); 72 else add(l[px], 1); 73 vis[px] = !vis[px]; 74 } 75 else 76 { 77 scanf("%d", &px); 78 int ans = sum(r[px]) - sum(l[px]-1); 79 printf("%d\n", ans); 80 } 81 } 82 return 0; 83 } 84 */ 85 86 ///建樹+樹狀數組(靜態連接表) 4940k 454ms 87 #include <cstdio> 88 #include <iostream> 89 #include <cstring> 90 #include <vector> 91 #include <algorithm> 92 #define INF 0x3f3f3f3f 93 using namespace std; 94 95 const int MAXN = 1e5+10; 96 const int MAXM = 1e5+10; 97 98 struct node 99 { 100 int to, next; 101 }edge[MAXM<<1]; 102 103 int head[MAXN]; 104 bool vis[MAXN]; 105 int t[MAXN], l[MAXN], r[MAXN]; 106 int N, M, cnt, key; 107 108 int lowbit(int x) 109 { 110 return x&(-x); 111 } 112 void add(int x, int value) 113 { 114 for(int i = x; i <= N; i+=lowbit(i)) 115 t[i]+=value; 116 } 117 int sum(int x) 118 { 119 int res = 0; 120 for(int i = x; i > 0; i-=lowbit(i)) 121 res+=t[i]; 122 return res; 123 } 124 125 void dfs(int k) 126 { 127 l[k] = key; 128 for(int i = head[k]; i != -1; i = edge[i].next) 129 { 130 if(l[edge[i].to]) continue; 131 key++; 132 dfs(edge[i].to); 133 } 134 r[k] = key; 135 } 136 137 void init() 138 { 139 memset(head, -1, sizeof(head)); 140 cnt = 1; 141 } 142 143 void add_e(int u, int v) 144 { 145 edge[cnt].to = v; 146 edge[cnt].next = head[u]; 147 head[u] = cnt++; 148 } 149 150 int main() 151 { 152 scanf("%d", &N); 153 init(); 154 int a, b; 155 for(int i = 1; i <= N; i++) 156 { 157 vis[i] = 1; //每個節點一開始都有蘋果 158 add(i, 1); //初始化樹狀數組 159 } 160 for(int i = 1; i < N; i++) 161 { 162 scanf("%d%d", &a, &b); ///建無向圖 163 add_e(a, b); 164 add_e(b, a); 165 } 166 key = 1; dfs(1); //建樹 167 scanf("%d", &M); 168 int px; 169 char com[3]; 170 while(M--) 171 { 172 scanf("%s", &com); 173 if(com[0] == ‘C‘) 174 { 175 scanf("%d", &px); 176 if(vis[px]) add(l[px], -1); 177 else add(l[px], 1); 178 vis[px] = !vis[px]; 179 } 180 else 181 { 182 scanf("%d", &px); 183 printf("%d\n", sum(r[px])-sum(l[px]-1)); 184 } 185 } 186 return 0; 187 }
POJ 3321 Apple Tree 【樹狀數組+建樹】