Codevs1128蘋果樹題解

來源:互聯網
上載者:User

標籤:線段樹   樹狀數組   

  • 題目描述 Description
    在卡卡的房子外面,有一棵蘋果樹。每年的春天,樹上總會結出很多的蘋果。卡卡非常喜歡吃蘋果,所以他一直都精心的呵護這棵蘋果樹。我們知道樹是有很多分叉點的,蘋果會長在枝條的分叉點上面,且不會有兩個蘋果結在一起。卡卡很想知道一個分叉點所代表的子樹上所結的蘋果的數目,以便研究蘋果樹哪些枝條的結果能力比較強。
    卡卡所知道的是,每隔一些時間,某些分叉點上會結出一些蘋果,但是卡卡所不知道的是,總會有一些調皮的小孩來樹上摘走一些蘋果。
    於是我們定義兩種操作:
    C x
    表示編號為x的分叉點的狀態被改變(原來有蘋果的話,就被摘掉,原來沒有的話,就結出一個蘋果)
    Qx
    查詢編號為x的分叉點所代表的子樹中有多少個蘋果
    我們假定一開始的時候,樹上全都是蘋果,也包括作為根結點的分叉1。

  • 輸入描述 Input Description
    第一行一個數N (n<=100000)
    接下來n-1行,每行2個數u,v,表示分叉點u和分叉點v是直接相連的。
    再接下來一行一個數M,(M<=100000)表示詢問數
    接下來M行,表示詢問,詢問的格式如題目所述Q x或者C x

  • 輸出描述 Output Description
    對於每個Q x的詢問,請輸出相應的結果,每行輸出一個

  • 範例輸入 Sample Input
    3
    1 2
    1 3
    3
    Q 1
    C 2
    Q 1

  • 範例輸出 Sample Output
    3
    2

  • 題解
  • 一看這資料規模和輸入方式就基本可以確定是一道進階資料結構題。做了不少關於樹的題目,自己感覺樹其實對應著一段區間,即其遍曆順序,很多種維護方式可以基於這個遍曆順序進行
  • 二叉樹的中序遍曆比較特殊,對此Noip2003加分二叉樹一題已充分體現。那個題目用了中序遍曆進行區間dp,並取得了良好的效果。
  • 而普通的樹並沒有中序遍曆,但其本身的先根遍曆與後根遍曆也有諸多特殊性。
  • 先對樹進行一次深度優先遍曆,用dfn數組(時間戳記)標記整棵樹遍曆時訪問結點的順序,因為一棵子樹的訪問順序是連續的!
  • 這樣一棵子樹就可以被當作一段區間來提取。本題只涉及樹上單個結點的修改與和的查詢,所以立刻可以想到樹狀數組和線段樹。
  • 如果使用先根遍曆,那麼需要記錄每棵子樹下最被訪問到的結點的時間戳記,這樣這棵子樹可以用子樹的根的時間戳記子樹下最被訪問到的結點的時間戳記在先根遍曆得到的序列中提取出來。同理,使用後根遍曆需要記錄每棵子樹最被訪問到的結點的時間戳記
  • 下面就好解決了:
    C操作,直接對著時間戳記修改在遍曆順序中相應的結點即可;
    Q操作,直接求時間戳記在遍曆順序中對應的區間和即可;

  • Code——————用zkw線段樹維護後根遍曆序列

#include <cstdio>#include <cstring>#include <vector>#include <iostream>#include <algorithm>using namespace std;const int maxn = 100010, nil = 0, root = 1;int n, m, lc[maxn], dfn[maxn];int T[maxn << 2], delta, tot;   //zkw線段樹,tot是訪問順序vector <int> tree[maxn];bool *vis;                      //因為不明確給出的邊的父子關係,所以雙向添邊,遍曆時這個vis記錄結點是否被訪問過int dfs(int rot)                //返回以rot為根的子樹的最左邊的兒子{    int rt = 0;    bool b = false;    vis[rot] = true;    for(int i = 0; i < tree[rot].size(); ++i)        if(!vis[tree[rot][i]])        {            b = true;            rt = dfs(tree[rot][i]);            if(lc[rot] == nil) lc[rot] = rt;        }    dfn[rot] = ++tot;    if(b) return lc[rot];    else return lc[rot] = dfn[rot];}void init(){    int u, v;    cin >> n;    //線段樹初始化    for(delta = 2; delta <= (n << 1) + 1; delta <<= 1);    for(int i = delta + 1; i <= delta + n; ++i) T[i] = 1;    for(int i = delta - 1; i >= 1; --i) T[i] = T[i << 1] + T[i << 1 | 1];    //初始化結束    for(int i = 1; i < n; ++i)    {        cin >> u >> v;        tree[u].push_back(v);        tree[v].push_back(u);    }    vis = new bool[maxn];    memset(vis, false, sizeof(vis));    dfs(root);      delete [] vis; vis = NULL;    for(int i = 1; i <= n; ++i) tree[i].clear();    //少用點記憶體,對評測機溫柔點    cin >> m;}void change(int x)              //單點修改{    for(T[x += delta] ^= 1, x >>= 1; x; x >>= 1) T[x] = T[x << 1] + T[x << 1 | 1];}int query(int l, int r)         //區間查詢{    if(l == r) return T[l + delta];    int ans = 0;    for(l += delta - 1, r += delta + 1; l ^ r ^ 1; l >>= 1, r >>= 1)    {        if(~l & 1) ans += T[l ^ 1];        if(r & 1) ans += T[r ^ 1];    }    return ans;}void work(){    char opt;    int x;    while(m--)    {        cin >> opt >> x;        if(opt == ‘Q‘) cout << query(lc[x], dfn[x]) << endl;        else change(dfn[x]);    }}int main(){    init();    work();    return 0;}

Codevs1128蘋果樹題解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.