1. 程式人生 > >POJ 3321 Apple Tree 【樹狀數組+建樹】

POJ 3321 Apple Tree 【樹狀數組+建樹】

size stream typedef val nth beginning emp get 鄰接

題目鏈接: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

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

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 【樹狀數組+建樹】