1. 程式人生 > >poj3321-Apple Tree(DFS序+樹狀數組)

poj3321-Apple Tree(DFS序+樹狀數組)

ise 發現 ostream center 表示 des mil bmi integer

Apple Tree
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 36442 Accepted: 10894

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-n,如上圖所示,初始狀態時,每個的樹杈位置都有一個蘋果,有m個操作,操作(c num)是如果num號樹杈有蘋果,就摘掉,否則這個樹杈上就會長出蘋果;操作(Q num)表示求以第num個樹杈為根的子樹共有多少蘋果。
思路:這題可以用dfs序給樹杈重新標號,使得每一棵子樹的序號都是連續的,然後就將樹狀結構轉換為了線性結構,再使用樹狀數組求和就是求子樹的所有蘋果數了。

DFS序粗講:所謂dfs序就是一棵樹上每個節點在dfs先序遍歷中的進出棧的時間序列。如下面這棵樹:
技術分享圖片

它的dfs序就是:

技術分享圖片

上面的遍歷結果中每個點都出現兩次,因為一次是進棧,一次出棧。

我們可以發現,以每個節點作為根節點的子樹,這棵子樹中的所有節點在dfs序中都處於根節點中間。

例如:以B為根節點的子樹,有BEFK四個點,而在dfs序中,這四個點的遍歷順序是相連的,為BEEFKKFB,EFK作為子節點,遍歷的順序在B進棧之後,而又在B出棧之前。驗證可以發現,每一個節點都滿足這一性值。

所以,我們按照dfs序重新給各個節點編號,每個節點記錄兩個值,一個是進棧的時間,一個是出棧的時間。如下圖:

技術分享圖片

這棵樹按dfs先序遍歷之後獲得了如上圖的編號,每個點都有其進棧的時間和出棧的時間,兩者形成了一個區間,圖中每個點的進棧時間都不同,我們就以進棧時間點作為新的編號(根據寫法不同也可以是出棧時間各不同)。我們可以發現,若以某個節點作為根節點,那麽它的子樹上的所有點的的編號都在根節點的區間範圍內。例如上圖中的點4,它的新編號為5,區間5-7,它的兩個子節點編號分別為6,7;節點2的編號為2,區間為2-3,子節點新編號為3(舊編號為5)。

知道這一點之後,我們就可以來求以某個節點為根的子樹的值的和了。比如求上圖中4號節點為根的子樹的和,則就是求新編號區域為5-7的和,這是連續的,用樹狀數組就行了,也即是sum(7)-sum(4)。

參考博客:https://blog.csdn.net/qq_39670434/article/details/78425125

具體操作看代碼:

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #include<string>
  5 #include<cmath>
  6 #include<algorithm>
  7 #include<stack>
  8 #include<climits>
  9 #include<map> 
 10 #include<queue>
 11 #define eps 1e-7
 12 #define ll long long
 13 #define inf 0x3f3f3f3f
 14 #define pi 3.141592653589793238462643383279
 15 using namespace std;
 16 const int MAXN = 1e5+7;
 17 
 18 struct Edge{
 19     int next,to;
 20 }edge[MAXN];
 21 int head[MAXN],n,m,cnt,in[MAXN],out[MAXN],visit[MAXN],c[MAXN];
 22 
 23 void ADD(int beg,int end) //鏈式前向心建樹 
 24 {
 25     edge[cnt].next = head[beg];
 26     edge[cnt].to = end;
 27     head[beg] = cnt++;
 28 }
 29 
 30 void DFS(int u) //獲得dfs序 
 31 {
 32     visit[u] = 1; //標記節點已經被範圍(此題可以沒有) 
 33     in[u] = ++cnt; //記錄節點u進棧的時間 
 34     for(int i=head[u]; i!=-1; i=edge[i].next) //遍歷節點u所有的子節點 
 35     {
 36         int j = edge[i].to;  
 37         if(!visit[j]) DFS(j); //搜索子節點 
 38     }
 39     out[u] = cnt; //記錄節點u出棧的時間 
 40 }
 41 
 42 int lowBit(int x)
 43 {
 44     return x&(-x);
 45 }
 46 
 47 void add(int x,int num)
 48 {
 49     for(int i=x; i<=n; i+=lowBit(i))
 50         c[i] += num;
 51 }
 52 
 53 int query(int x)
 54 {
 55     int ans = 0;
 56     for(int i=x; i>0; i-=lowBit(i))
 57         ans += c[i];
 58     return ans;
 59 }
 60 
 61 int main()
 62 {
 63     int t;
 64     char ques[2];
 65     while(scanf("%d",&n)!=EOF)
 66     {
 67         fill(head,head+n+5,-1);
 68         fill(visit,visit+n+5,0);
 69         cnt = 0;
 70         
 71         int beg,end;
 72         for(int i=0; i<n-1; ++i)
 73         {
 74             scanf("%d%d",&beg,&end);
 75             ADD(beg,end);
 76         }
 77         cnt = 0;
 78         DFS(1); //dfs獲得新的編號 
 79 
 80         c[0] = 0;    
 81         for(int i=1; i<=n; ++i) add(i,1); //樹狀數組給每個節點賦值,因為題目初始狀態是每個節點都有蘋果 
 82         
 83         cin>>m;
 84         while(m--)
 85         {
 86             scanf("%s%d",ques,&t);
 87             if(ques[0] == C)
 88             {
 89                 if(visit[t] == 1) //判斷此節點是否有蘋果 
 90                 {
 91                     
 92                     add(in[t],-1); //有蘋果就減去 
 93                     visit[t] = 0; //然後標記為沒有蘋果 
 94                 }
 95                 else
 96                 {
 97                     add(in[t],1); //沒有蘋果就加上 
 98                     visit[t] = 1;  //然後標記為有 
 99                 }
100             }
101             else
102             {
103                 int ans = query(out[t]) - query(in[t]-1); //計算子樹的蘋果數 
104                 printf("%d\n",ans);
105             }
106         }
107     }
108     return 0;
109 }

poj3321-Apple Tree(DFS序+樹狀數組)