Splay樹筆記 poj 3486

來源:互聯網
上載者:User

    **學習資源:

             UnGeek          

             kuangbin總結           

             國外好玩的splay demo(什麼策略能把一棵splay樹拉成一條鏈呢)

                   兩篇論文: 楊思雨《伸展樹的基本操作與應用》   Crash《運用伸展樹解決數列維護問題》


    ** 把kuangbin的數組形式改成結構題形式怎麼比原來快了1s ?!neko說結構體的緩衝利用率較高=_=

    **區間操作
    首先要有一個認識,Splay樹的中序遍曆即為我們要維護的序列。即Splay樹的伸展操作並沒有改變各個節點的相對位置(從中序的角度)。   這是理解區間操作正確性的前提條件。這沒什麼好證明的,自己類比一遍zig/zig-zig/zig-zag就理解了。

    提取區間[a,b] : 們將a前面一個數對應的節點旋轉到根節點,將b後面一個數對應的節點轉到樹根的右孩子,那麼根的右孩子的左孩子就對應了[a,b]。這裡找第k個節點Get_Kth(root, k)是根據節點的size來找的,不是根據value。
    也正因為Get_Kth()是根據size,也就是區間長度來找節點的位置的,而我們在Splay操作的時候也有好好地維護節點的size值,所以在做完上述的操作後,Key_value就對應了區間[a,b],這個“區間”跟線段樹的“區間”相同點在於,這個節點的屬性都代表了該區間的屬性,不同點在於,因為線段樹是按下標排的,所以區間節點的下標有序,但splay樹的區間節點的下標只是相對有序。
    "將a前面一個數對應的節點旋轉到根節點"對應代碼: Splay(Get_Kth(root, l), 0);     
     解釋:father 為0,因為root根節點的father是0,做完這步Splay操作之後新root為下標l對應的節點

    "將b後面一個數對應的節點轉到樹根的右孩子"對應代碼: Splay(Get_Kth(root, r+2), root);
     解釋:father 為root,因為r-2 > l,新root是下標l對應的節點,所以Get_Kth(root, r+2)出來的節點一定在新root的右邊,所以這一步Splay(Get_Kth(root, r+2), root) 才表示旋轉r+2對應節點到root的右孩子,而不是左孩子。
     注意,因為在代碼裡面插入了兩個邊界點,所以找a前面一個數的節點是Get_Kth(root, a-1+1),b後面一個數是Get_Kth(root, b+1+1).  

    ** 代碼裡面兩個地方傳的是引用,一個是NewNode(),一個是Build().

    ** 為什麼代碼在旋轉的時候只對節點的父親維護不對x節點維護,但Splay操作的最後卻有維護了x節點?
     解釋:直接摘抄Crash的解釋:“因為除了一字形旋轉,在 Splay 操作裡我們進行的旋轉都只對 X 結點進行,因此過早地維護是多餘的;而在一字形旋轉中,好像在旋轉中沒有對 X 的父親進行維護,但後面緊接著就是旋轉 X 結點,又會對 X 的父親進行維護,也是沒問題的。這樣可以節省不少冗餘的 Update 操作,能減小程式隱含的常數。”

    ** 記憶體池類比:這一題沒用到,就是用一個雙端隊列類比一下而已。
       rear != 0表示有拉圾記憶體塊,則在建立節點的時候覆用這塊記憶體,否則開新記憶體,if(rear != 0) x = mem[rear--]; else x = head++;

    ** 抱著猜測的態度注釋掉了Update和Init下的Push_up(root),Push_up(f[root].ch[1])也AC了,說明這是冗餘操作吧...=_=

    核心函數: NewNode(), Build(先建中端再建兩端的方法), Rotate(), Splay(將x調整到goal的下方), Get_Kth(), Init().
              #define   Key_value  ( f[f[root].ch[1]].ch[0] )

*/

下面是poj 3486 的ac代碼

#include <iostream>#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;#define         MID(l,r)        (((l) + (r)) >> 1)#define         Key_value       ( f[f[root].ch[1]].ch[0] )#define         MAXN            100010inline char rdc() { scanf(" "); return getchar(); }inline int  rdi() { int d; scanf("%d", &d); return d; } //void Put_self(int u, int value);class Node {    public :    int value, pre, size, add;    long long sum;    int ch[2];    Node () { }    Node(int pre, int size, int value, int add, long long sum, int l, int r) : pre(pre), size(size), value(value), add(add), sum(sum) {        ch[0] = l, ch[1] = r;    }} f[MAXN];int a[MAXN], mem[MAXN], head, rear, root, n;void Put_self(int u, int value){       if(u == 0) return ;    f[u].add += value, f[u].value += value, f[u].sum += (long long) value * f[u].size;}void Push_down(int u){    if(!f[u].add) return ;    Put_self(f[u].ch[0], f[u].add);    Put_self(f[u].ch[1], f[u].add);    f[u].add = 0;}void Push_up(int u){    f[u].size = 1 + f[f[u].ch[0]].size + f[f[u].ch[1]].size;    f[u].sum  = f[u].value + f[f[u].ch[0]].sum + f[f[u].ch[1]].sum;}void Rotate(int x, int c){    int y = f[x].pre;    Push_down(y), Push_down(x);    f[y].ch[!c] = f[x].ch[c];    f[f[x].ch[c]].pre = y;    if(f[y].pre) {        if(f[f[y].pre].ch[0] == y) f[f[y].pre].ch[0] = x;        else f[f[y].pre].ch[1] = x;    }    f[x].pre = f[y].pre;    f[y].pre = x;    f[x].ch[c] = y;    Push_up(y);         //維護y節點}void Splay(int x, int goal){    Push_down(x);    while(f[x].pre != goal) {        int y = f[x].pre;        if(f[y].pre == goal) Rotate(x, f[y].ch[0] == x);        else {            int z = f[y].pre, c = (f[z].ch[0] == y);            if(f[y].ch[c] == x) Rotate(x, !c), Rotate(x, c);    //之字形旋轉            else Rotate(y, c), Rotate(x, c);                    //一字形旋轉        }    }    Push_up(x);        //維護x節點    if(goal == 0) root = x;}void NewNode(int &u, int father, int value)      //u是引用!{    if(rear) u = mem[rear--]; else u = ++head;    f[u] = Node(father, 1, value, 0, 0, 0, 0);   }void Build(int &x, int l, int r, int father)         //x是引用!{   if(l > r) return ;   int mid = MID(l, r);   NewNode(x, father, a[mid]);   Build(f[x].ch[0], l, mid-1, x), Build(f[x].ch[1], mid+1, r, x);   Push_up(x);}void Init(){    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);    root = 0;    rear = head = 0;    f[root] = Node(0, 0, 0, 0, 0, 0, 0);    NewNode(root, 0, -1);    NewNode(f[root].ch[1], root, -1);    Build(Key_value, 1, n, f[root].ch[1]);}int Get_Kth(int u, int k){    Push_down(u);    int t = f[f[u].ch[0]].size + 1;    if(t == k) return u;    else if(t > k) return Get_Kth(f[u].ch[0], k);    else return Get_Kth(f[u].ch[1], k - t);}void Update(int l, int r, int value){    Splay(Get_Kth(root, l), 0);     //第l個節點到根節點     因為root根節點的father是0       做完這步操作之後新root為下標l對應的節點                                    //而因為r-2 > l,所以Get_Kth(root, r+2)出來的節點一定在新root的右邊,所以下一步Splay(Get_Kth(root, r+2), root) 才表示旋轉r+2對應節點到根節點的右孩子,而不是左孩子。提醒:記住這一步之後root節點變了;Get_Kth()是根據size來找的.    Splay(Get_Kth(root, r+2), root);    //第r+2個點到根節點的右孩子         Put_self(Key_value, value);}long long Query(int l, int r){    Splay(Get_Kth(root, l), 0);    Splay(Get_Kth(root, r+2), root);    return f[Key_value].sum;}int main(){    int query, l, r;    while(scanf("%d%d", &n, &query) != EOF) {        Init();        while(query--) {            char op = rdc();            scanf("%d%d", &l, &r);            if(op == 'Q') {                printf("%I64d\n", Query(l, r));            } else {                Update(l, r, rdi());            }        }    }    return 0;} 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.