Splay Tree(伸展樹)

分類:IT技術 時間:2016-10-17

參考:《數據結構(C++語言版)》鄧俊輝著 (好書

一、

伸展樹(由 D. D. Sleator 和 R. E. Tarjan 於 1985 年發明)也是平衡二叉搜索樹的一種形式。相對於 AVL 樹,伸展樹的實現更為簡潔

伸展樹無需時刻都嚴格地保持全樹的平衡,但卻能夠在任何足夠長的真實操作序列中,保持分攤意義上的高效率

伸展樹也不需要對基本的二叉樹節點結構做任何附加的要求或改動,更不需要記錄平衡因子或高度之類的額外信息,故適用範圍更廣


二、局部性

信息處理的典型模式是,將所有的數據項視作一個集合,並將其組織為某種適宜的數據結構,進而借助操作接口高效訪問

為考查和評價各操作接口的效率,除了從最壞情況的角度出發,也可假定所有操作彼此獨立、次序隨機且概率相等,並從平均情況的角度出發

然而,後一尺度所依賴的假定條件, 往往並不足以反映真實的情況

實際上, 通常在任意數據結構的生命期內, 不僅執行不同操作的概率往往極不均衡,而且各操作之間具有極強的相關性, 並在整體上多呈現出極強的規律性

其中最為典型的,就是所謂的“數據局部性”( data locality) ,這包括兩個方面的含義:

  1. 剛剛被訪問過的元素, 極有可能在不久之後再次被訪問到
  2. 將被訪問的下一元素, 極有可能就處於不久之前被訪問過的某個元素的附近

充分利用好此類特性,即可進一步地提高數據結構和算法的效率

就二叉搜索樹而言,數據局部性具體表現為:

  1. 剛剛被訪問過的節點, 極有可能在不久之後再次被訪問到
  2. 將被訪問的下一節點, 極有可能就處於不久之前被訪問過的某個節點的附近

因此, 只需將剛被訪問的節點,及時地“轉移”至樹根( 附近) , 即可加速後續的操作

當然,轉移前後的搜索樹必須相互等價


三、逐層伸展

1、簡易伸展樹

一種直接方式是:每訪問一個節點之後,隨即反復地以它的父節點為軸,經適當的旋轉將其提升一層,直至最終成為樹根

以圖 1 為例,若深度為 3 的節點 E 剛被訪問 -- 無論查找或插入,甚至“刪除” -- 都可通過 3 次旋轉,將該樹等價變換為以 E 為根的另一棵二叉搜索樹


圖 1 通過自下而上的一系列等價變換,可使任一節點上升至樹根


隨著節點 E 的逐層上升,兩側子樹的結構也不斷地調整,故這一過程也稱作伸展(splaying)

而采用這一調整策略的二叉搜索樹也因此得名

不過,為實現真正意義上的伸展樹,還須對以上策略做點微秒而本質的改進

之所以改進,是因為目前的策略仍存在致命的缺陷 -- 對於很多訪問序列,單次訪問的分攤時間復雜度在極端情況下可能高達 Ω(n)


2、最壞情況

不難驗證,若從空樹開始依次插入關鍵碼 {1,2,3,4,5},且其間采用如上調整策略,則可得到如圖 2 所示的二叉搜索樹

接下來,若通過 search() 接口,再由小到大地依次訪問各節點一次,則該樹在各次訪問之後的結構形態將如圖(b ~ f)所示


圖 2 簡易伸展樹的最壞情況


可見,在各次訪問之後, 為將對應節點伸展調整至樹根,分別需做 4、 4、 3、 2 和 1 次旋轉

一般地,若節點總數為 n,則旋轉操作的總次數應為:

(n - 1) + { (n - 1) + (n - 2) + ... + 1 } = (n^2 + n - 2) / 2 = Ω(n^2)

如此分攤下來,每次訪問平均需要 Ω(n) 時間

很遺憾,這一效率不僅遠遠低於 AVL 樹, 而且甚至與原始的二叉搜索樹的最壞情況相當

而事實上,問題還遠不止於此

稍做比對即不難發現, 圖 2(a) 與 (f) 中二叉搜索樹的結構完全相同

也就是說,經過以上連續的 5 次訪問之後, 全樹的結構將會復原!這就意味著,以上情況可以持續地再現

當然, 這一實例, 完全可以推廣至規模任意的二叉搜索樹

於是對於規模為任意 n 的伸展樹,只要按關鍵碼單調的次序, 周期性地反復進行查找

則無論總的訪問次數 m >> n 有多大, 就分攤意義而言, 每次訪問都將需要 Ω(n) 時間!

那麽,這類最壞的訪問序列能否回避?具體地,又應該如何回避?


四、雙層伸展

為克服上述伸展調整策略的缺陷,一種簡便且有效的方法就是:將逐層伸展改為雙層伸展

具體地,每次都從當前節點v向上追溯兩層(而不是僅一層) , 並根據其父親p以及祖父g的相對位置, 進行相應的旋轉

以下分三類情況, 分別介紹具體的處理方法

1、zig-zig / zag-zag

如圖 3(a) 所示,設 v 是 p 的左孩子,且 p 也是 g 的左孩子

設 W 和 X 分別是 v 的左、右子樹, Y 和 Z 分別是 p 和 g 的右子樹


圖 3 通過 zig-zig 操作,將節點v上推兩層


針對這種情況,首先以節點 g 為軸做順時針旋轉 zig(g), 其效果如圖 (b) 所示

然後,再以 p 為軸做順時針旋轉 zig(p), 其效果如圖 (c) 所示

如此連續的兩次 zig 旋轉, 合稱 zig-zig 調整

自然地, 另一完全對稱的情形 -- v 是 p 的右孩子,且 p 也是 g 的右孩子 -- 則可通過連續的兩次逆時針旋轉實現調整, 合稱 zag-zag 操作


2、zig-zag / zag-zig

如圖 4(a) 所示,設 v 是 p 的左孩子,而 p 是 g 的右孩子

設 W 是 g 的左子樹, X 和 Y 分別是 v 的左、右子樹, Z 是 p 的右子樹


圖 4 通過 zig-zag 操作,將節點 v 上推兩層


針對這種情況,首先以節點 p 為軸做順時針旋轉 zig(p), 其效果如 (b) 所示

然後,再以 g 為軸做逆時針旋轉 zag(g), 其效果如圖 (c) 所示

如此 zig 旋轉再加 zag 旋轉, 合稱 zig-zag 調整

同樣地, 另一完全對稱的情形 -- v 是 p 的右孩子,而 p 是 g 的左孩子 -- 則可通過 zag 旋轉再加 zig 旋轉實現調整, 合稱 zag-zig 操作


3、zig / zag

如圖 5(a) 所示,若 v 最初的深度為奇數,則經過若幹次雙層調整至最後一次調整時, v 的父親 p 即是樹根 r

將 v 的左、右子樹記作 X 和 Y,節點 p = r 的另一子樹記作 Z


圖 5 通過 zig 操作,將節點 v 上推一層,成為樹根


此時,只需圍繞 p = r 做順時針旋轉 zig(p),即可如圖 (b) 所示, 使 v 最終攀升至樹根,從而結束整個伸展調整的過程

zag 調整與之對稱


4、效果與效率

綜合以上各種情況,每經過一次雙層調整操作,節點 v 都會上升兩層

若v的初始深度 depth(v) 為偶數,則最終 v 將上升至樹根

若 depth(v) 為奇數,則當 v 上升至深度為 1 時,不妨最後再相應地做一次 zig 或 zag 單旋操作

無論如何,經過 depth(v) 次旋轉後, v 最終總能成為樹根


重新審視圖 2 的最壞實例不難發現,這一訪問序列導致 Ω(n) 平均單次訪問時間的原因,可以解釋為:

在這一可持續重復的過程中,二叉搜索樹的高度始終不小於 n/2

而且,至少有一半的節點在接受訪問時,不僅沒有如最初設想的那樣靠近樹根, 而且反過來恰恰處於最底層


從樹高的角度看,問題根源也可再進一步地解釋為:

在持續訪問的過程中, 樹高依算術級數逐步從 n - 1 遞減至 n/2 ,然後再逐步遞增回到 n - 1


那麽, 采用上述雙層伸展的策略將每一剛被訪問過的節點推至樹根, 可否避免如圖 2 所示的最壞情況呢?


稍作對比不難看出,就調整之後的局部結構而言, zig-zag 和 zag-zig 調整與此前的逐層伸展完全一致( 亦等效於 AVL 樹的雙旋調整),而 zig-zig 和 zag-zag 調整則有所不同

事實上,後者才是雙層伸展策略優於逐層伸展策略的關鍵所在

以如圖 6(b) 所示的二叉搜索樹為例,在 find(1) 操作之後采用逐層調整策略與雙層調整策略的效果, 分別如圖 (a) 和圖 (c) 所示


圖 6 雙層調整策略的高度折半效果


可見,最深節點( 1)被訪問之後再經過雙層調整, 不僅同樣可將該節點伸展至樹根,而且同時可使樹的高度接近於減半

就樹的形態而言,雙層伸展策略可“ 智能” 地“折疊” 被訪問的子樹分支,從而有效地避免對長分支的連續訪問

這就意味著, 即便節點 v 的深度為 Ω(n),雙層伸展策略既可將 v 推至樹根, 亦可令對應分支的長度以幾何級數( 大致折半)的速度收縮


圖 7 則給出了一個節點更多、更具一般性的例子,從中可更加清晰地看出這一效果


圖 7 伸展樹中較深的節點一旦被訪問到,對應分支的長度將隨即減半


盡管在任一時刻伸展樹中都可能存在很深的節點,但與含羞草類似地, 一旦這類“ 壞” 節點被“碰觸” 到, 經過隨後的雙層伸展, 其對應的分支都會收縮至長度大致折半

於是, 即便每次都“ 惡意地” 試圖訪問最底層節點,最壞情況也不會持續發生

可見,伸展樹雖不能杜絕最壞情況的發生, 卻能有效地控制最壞情況發生的頻度,從而在分攤意義下保證整體的高效率

更準確地, Tarjan 等人采用勢能分析法( potential analysis)也已證明,在改用“雙層伸展”策略之後,伸展樹的單次操作均可在分攤的 O(logn) 時間內完成


BZOJ 1588 [HNOI2002]營業額統計

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <cmath>
#include <cctype>
#include <bitset>
#include <ctime>

using namespace std;

#define REP(i, n) for (int i = 0; i < (n); ++i)

typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef pair<int, int> Pair;

const ull mod = 1e9 + 7;
const int INF = 0x7fffffff;
const int maxn = 1e5;

int son[maxn][2], pre[maxn], key[maxn], root, size;

void New_Node(int &r, int father, int x);
bool insert(int x);
void Rotate(int x, int kind);
void Splay(int x, int goal);
int Succ(int x);
int Pred(int x);

int main()
{
#ifdef __AiR_H
    freopen("in.txt", "r", stdin);
#endif // __AiR_H
    int n, num, ans = 0;
    root = size = 0;
    scanf("%d", &n);
    scanf("%d", &num);
    ans += num;
    insert(num);
    --n;
    while (n--) {
        scanf("%d", &num);
        if (insert(num)) {
            int pre = Pred(root);
            int next = Succ(root);
            if (pre == INF) {
                ans += next - num;
            } else if (next == INF) {
                ans += num - pre;
            } else {
                ans += min(next-num, num-pre);
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

int Pred(int x)
{
    int lmax = son[x][0];
    if (lmax == 0) {
        return INF;
    }
    while (son[lmax][1]) {
        lmax = son[lmax][1];
    }
    return key[lmax];
}

int Succ(int x)
{
    int rmin = son[x][1];
    if (rmin == 0) {
        return INF;
    }
    while (son[rmin][0]) {
        rmin = son[rmin][0];
    }
    return key[rmin];
}

void Splay(int x, int goal)
{
    while (pre[x] != goal) {
        if (pre[pre[x]] == goal) {
            Rotate(x, son[pre[x]][0] == x);
        } else {
            int y = pre[x];
            int kind = son[pre[y]][0] == y;
            if (son[y][kind] == x) {
                Rotate(x, !kind);
                Rotate(x, kind);
            } else {
                Rotate(y, kind);
                Rotate(x, kind);
            }
        }
    }
    if (goal == 0) {
        root = x;
    }
}

void Rotate(int x, int kind)
{
    int y = pre[x];
    son[y][!kind] = son[x][kind];
    pre[son[x][kind]] = y;
    if (pre[y]) {
        son[pre[y]][son[pre[y]][1] == y] = x;
    }
    pre[x] = pre[y];
    son[x][kind] = y;
    pre[y] = x;
}

bool insert(int x)
{
    int pos = root;
    while (son[pos][x > key[pos]]) {
        if (key[pos] == x) {
            Splay(pos, 0);
            return false;
        }
        pos = son[pos][x > key[pos]];
    }
    New_Node(son[pos][x > key[pos]], pos, x);
    Splay(size, 0);
    return true;
}

void New_Node(int &r, int father, int x)
{
    r = ++size;
    pre[r] = father;
    key[r] = x;
    son[r][0] = son[r][1] = 0;
}

BZOJ 1503 [NOI2004]郁悶的出納員

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <cmath>
#include <cctype>
#include <bitset>
#include <ctime>

using namespace std;

#define REP(i, n) for (int i = 0; i < (n); ++i)

typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef pair<int, int> Pair;

const ull mod = 1e9 + 7;
const int INF = 0x7fffffff;
const int maxn = 1e5;

struct splay {
    int son[maxn][2], pre[maxn], key[maxn], sum[maxn], root, size;
    void Init();
    void New_Node(int &r, int father, int x);
    void insert(int x);
    int remove(int x);
    int find_by_order(int k);
    void Rotate(int x, int kind);
    void Splay(int x, int goal);
    int Succ(int x);
    int find(int x);
};

int main()
{
#ifdef __AiR_H
    freopen("in.txt", "r", stdin);
#endif // __AiR_H
    int n, Min, k, adjust = 0, ans = 0;
    splay tree;
    tree.Init();
    char cmd[1];
    scanf("%d %d", &n, &Min);
    while (n--) {
        scanf("%s %d", cmd, &k);
        if (cmd[0] == 'I') {
            if (k >= Min) {
                tree.insert(k - adjust);
            }
        } else if (cmd[0] == 'A') {
            adjust += k;
        } else if (cmd[0] == 'S') {
            adjust -= k;
            ans += tree.remove(tree.Succ(Min - adjust));
        } else {
            int sum = tree.sum[tree.root];
            if (k > (sum - 2)) {
                printf("-1\n");
            } else {
                printf("%d\n", tree.find_by_order(sum - 1 - k) + adjust);
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

void splay::Init()
{
    size = root = 0;
    New_Node(root, 0, -INF);
    New_Node(son[root][1], root, INF);
    sum[root] = 2;
}

int splay::find_by_order(int k)
{
    int pos = root;
    while (sum[son[pos][0]] != k) {
        if (sum[son[pos][0]] > k) {
            pos = son[pos][0];
        } else {
            k -= (sum[son[pos][0]] + 1);
            pos = son[pos][1];
        }
    }
    Splay(pos, 0);
    return key[pos];
}

int splay::Succ(int x)
{
    int pos = root, Min = INF;
    while (pos) {
        if (key[pos] == x) {
            return x;
        }
        if (key[pos] > x) {
            Min = min(Min, key[pos]);
        }
        pos = son[pos][x > key[pos]];
    }
    return Min;
}

int splay::find(int x)
{
    int pos = root, ret = 0;
    while (pos) {
        if (key[pos] == x) {
            ret = pos;
        }
        pos = son[pos][x > key[pos]];
    }
    return ret;
}

int splay::remove(int x)
{
    int pos = find(x);
    Splay(1, 0);
    Splay(pos, 1);
    int ret = sum[son[pos][0]];
    son[pos][0] = 0;
    Splay(pos, 0);
    return ret;
}

void splay::Splay(int x, int goal)
{
    while (pre[x] != goal) {
        if (pre[pre[x]] == goal) {
            Rotate(x, son[pre[x]][0] == x);
        } else {
            int y = pre[x];
            int kind = son[pre[y]][0] == y;
            if (son[y][kind] == x) {
                Rotate(x, !kind);
                Rotate(x, kind);
            } else {
                Rotate(y, kind);
                Rotate(x, kind);
            }
        }
    }
    sum[x] = sum[son[x][0]] + sum[son[x][1]] + 1;
    if (goal == 0) {
        root = x;
    }
}

void splay::Rotate(int x, int kind)
{
    int y = pre[x];
    son[y][!kind] = son[x][kind];
    pre[son[x][kind]] = y;
    if (pre[y]) {
        son[pre[y]][son[pre[y]][1] == y] = x;
    }
    pre[x] = pre[y];
    son[x][kind] = y;
    pre[y] = x;
    sum[y] = sum[son[y][0]] + sum[son[y][1]] + 1;
}

void splay::insert(int x)
{
    int pos = root;
    while (son[pos][x > key[pos]]) {
        pos = son[pos][x > key[pos]];
    }
    New_Node(son[pos][x > key[pos]], pos, x);
    Splay(size, 0);
}

void splay::New_Node(int &r, int father, int x)
{
    r = ++size;
    pre[r] = father;
    key[r] = x;
    sum[r] = 1;
    son[r][0] = son[r][1] = 0;
}

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <cmath>
#include <cctype>
#include <bitset>
#include <ctime>

using namespace std;

#define REP(i, n) for (int i = 0; i < (n); ++i)

typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef pair<int, int> Pair;

const ull mod = 1e9 + 7;
const int INF = 0x7fffffff;
const int maxn = 1e5;

struct splay {
    int son[maxn][2], pre[maxn], key[maxn], sum[maxn], root, size;
    void Init();
    void New_Node(int &r, int father, int x);
    void insert(int x);
    int remove(int x);
    int find_by_order(int k);
    void Rotate(int x, int kind);
    void Splay(int x, int goal);
    int Succ(int x);
    int find(int x);
};

int main()
{
#ifdef __AiR_H
    freopen("in.txt", "r", stdin);
#endif // __AiR_H
    int n, Min, k, adjust = 0, ans = 0;
    splay tree;
    tree.Init();
    char cmd[1];
    scanf("%d %d", &n, &Min);
    while (n--) {
        scanf("%s %d", cmd, &k);
        if (cmd[0] == 'I') {
            if (k >= Min) {
                tree.insert(k - adjust);
            }
        } else if (cmd[0] == 'A') {
            adjust += k;
        } else if (cmd[0] == 'S') {
            adjust -= k;
            ans += tree.remove(tree.Succ(Min - adjust));
        } else {
            int sum = tree.sum[tree.root];
            if (k > sum) {
                printf("-1\n");
            } else {
                printf("%d\n", tree.find_by_order(sum + 1 - k) + adjust);
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

void splay::Init()
{
    size = root = 0;
}

int splay::find_by_order(int k)
{
    int pos = root;
    while (sum[son[pos][0]] + 1 != k) {
        if (sum[son[pos][0]] + 1 > k) {
            pos = son[pos][0];
        } else {
            k -= (sum[son[pos][0]] + 1);
            pos = son[pos][1];
        }
    }
    Splay(pos, 0);
    return key[pos];
}

int splay::Succ(int x)
{
    int pos = root, Min = INF;
    while (pos) {
        if (key[pos] == x) {
            return x;
        }
        if (key[pos] > x) {
            Min = min(Min, key[pos]);
        }
        pos = son[pos][x > key[pos]];
    }
    return Min;
}

int splay::find(int x)
{
    int pos = root, ret = 0;
    while (pos) {
        if (key[pos] == x) {
            ret = pos;
        }
        pos = son[pos][x > key[pos]];
    }
    return ret;
}

int splay::remove(int x)
{
    int pos = find(x), ret = 0;
    if (pos == 0) {
        ret = sum[root];
        size = root = 0;
        memset(son, 0, sizeof(son));
    } else {
        Splay(pos, 0);
        ret = sum[son[pos][0]];
        son[pos][0] = 0;
        Splay(pos, 0);
    }
    return ret;
}

void splay::Splay(int x, int goal)
{
    while (pre[x] != goal) {
        if (pre[pre[x]] == goal) {
            Rotate(x, son[pre[x]][0] == x);
        } else {
            int y = pre[x];
            int kind = son[pre[y]][0] == y;
            if (son[y][kind] == x) {
                Rotate(x, !kind);
                Rotate(x, kind);
            } else {
                Rotate(y, kind);
                Rotate(x, kind);
            }
        }
    }
    sum[x] = sum[son[x][0]] + sum[son[x][1]] + 1;
    if (goal == 0) {
        root = x;
    }
}

void splay::Rotate(int x, int kind)
{
    int y = pre[x];
    son[y][!kind] = son[x][kind];
    pre[son[x][kind]] = y;
    if (pre[y]) {
        son[pre[y]][son[pre[y]][1] == y] = x;
    }
    pre[x] = pre[y];
    son[x][kind] = y;
    pre[y] = x;
    sum[y] = sum[son[y][0]] + sum[son[y][1]] + 1;
}

void splay::insert(int x)
{
    int pos = root;
    while (son[pos][x > key[pos]]) {
        pos = son[pos][x > key[pos]];
    }
    New_Node(son[pos][x > key[pos]], pos, x);
    Splay(size, 0);
}

void splay::New_Node(int &r, int father, int x)
{
    r = ++size;
    pre[r] = father;
    key[r] = x;
    sum[r] = 1;
    son[r][0] = son[r][1] = 0;
}


BZOJ 1208 [HNOI2004]寵物收養所

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <cmath>
#include <cctype>
#include <bitset>
#include <ctime>

using namespace std;

#define REP(i, n) for (int i = 0; i < (n); ++i)

typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef pair<int, int> Pair;

const int mod = 1000000;
const int INF = 0x3fffffff;
const int maxn = 1e5;

struct splay {
    int son[maxn][2], pre[maxn], key[maxn], sum[maxn], root, size;
    void Init();
    void New_Node(int &r, int father, int x);
    void insert(int x);
    void remove(int x);
    void Rotate(int x, int kind);
    void Splay(int x, int goal);
    int Pred(int x);
    int Succ(int x);
    int find(int x);
};

int main()
{
#ifdef __AiR_H
    freopen("in.txt", "r", stdin);
#endif // __AiR_H
    splay tree;
    int n, a, b, ans = 0, num[2];
    num[0] = num[1] = 0;
    tree.Init();
    scanf("%d", &n);
    while (n--) {
        scanf("%d %d", &a, &b);
        if (num[!a] == 0) {
            ++num[a];
            tree.insert(b);
        } else {
            int pre = tree.Pred(b);
            int next = tree.Succ(b);
            if (b - pre <= next - b) {
                tree.remove(pre);
                ans += b - pre;
            } else {
                tree.remove(next);
                ans += next - b;
            }
            ans %= mod;
            --num[!a];
        }
    }
    printf("%d\n", ans);
    return 0;
}

void splay::Init()
{
    size = root = 0;
    New_Node(root, 0, -INF);
    New_Node(son[root][1], root, INF);
}

int splay::find(int x)
{
    int pos = root;
    while (pos) {
        if (key[pos] == x) {
            return pos;
        }
        pos = son[pos][x > key[pos]];
    }
    return 0;
}

int splay::Pred(int x)
{
    int pos = root, Max = -INF;
    while (pos) {
        if (key[pos] == x) {
            return x;
        }
        if (key[pos] < x) {
            Max = max(Max, key[pos]);
        }
        pos = son[pos][x > key[pos]];
    }
    return Max;
}

int splay::Succ(int x)
{
    int pos = root, Min = INF;
    while (pos) {
        if (key[pos] == x) {
            return x;
        }
        if (key[pos] > x) {
            Min = min(Min, key[pos]);
        }
        pos = son[pos][x > key[pos]];
    }
    return Min;
}

void splay::remove(int x)
{
    int pos = find(x);
    Splay(pos, 0);
    int t =  son[pos][1];
    while (son[t][0]) {
        t = son[t][0];
    }
    Splay(t, root);
    son[t][0] = son[root][0];
    pre[son[root][0]] = t;
    pre[t] = 0;
    root = t;
}

void splay::Splay(int x, int goal)
{
    while (pre[x] != goal) {
        if (pre[pre[x]] == goal) {
            Rotate(x, son[pre[x]][0] == x);
        } else {
            int y = pre[x];
            int kind = son[pre[y]][0] == y;
            if (son[y][kind] == x) {
                Rotate(x, !kind);
                Rotate(x, kind);
            } else {
                Rotate(y, kind);
                Rotate(x, kind);
            }
        }
    }
    sum[x] = sum[son[x][0]] + sum[son[x][1]] + 1;
    if (goal == 0) {
        root = x;
    }
}

void splay::Rotate(int x, int kind)
{
    int y = pre[x];
    son[y][!kind] = son[x][kind];
    pre[son[x][kind]] = y;
    if (pre[y]) {
        son[pre[y]][son[pre[y]][1] == y] = x;
    }
    pre[x] = pre[y];
    son[x][kind] = y;
    pre[y] = x;
    sum[y] = sum[son[y][0]] + sum[son[y][1]] + 1;
}

void splay::insert(int x)
{
    int pos = root;
    while (son[pos][x > key[pos]]) {
        pos = son[pos][x > key[pos]];
    }
    New_Node(son[pos][x > key[pos]], pos, x);
    Splay(size, 0);
}

void splay::New_Node(int &r, int father, int x)
{
    r = ++size;
    pre[r] = father;
    key[r] = x;
    sum[r] = 1;
    son[r][0] = son[r][1] = 0;
}


BZOJ 1500 [NOI2005]維修數列

參考:http://www.cnblogs.com/kuangbin/archive/2013/08/28/3287822.html

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#include <map>
#include <cmath>
#include <cctype>
#include <bitset>
#include <ctime>

using namespace std;

#define REP(i, n) for (int i = 0; i < (n); ++i)

typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef pair<int, int> Pair;

const ull mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int maxn = 5e5 + 10;

struct splay {
    int pre[maxn], son[maxn][2], key[maxn], size[maxn];
    int sum[maxn], reverse[maxn], same[maxn];
    int lx[maxn], rx[maxn], mx[maxn];
    int root, Count;
    void Init(void);
    void New_Node(int &r, int father, int _key);
    void Build(int &a, int low, int high, int father);
    void Push_Up(int r);
    void Push_Down(int r);
    void update_Rev(int r);
    void Update_Same(int r, int _key);
    void Rotate(int a, int kind);
    void Splay(int r, int goal);
    void erase(int r);
    void insert(int pos, int tot);
    void remove(int pos, int tot);
    int find_by_order(int r, int k);
    void Make_Same(int pos, int tot, int c);
    void Reverse(int pos, int tot);
    int Get_Sum(int pos, int tot);
    int Get_MaxSum(int pos, int tot);
};

splay tree;
int num[maxn];
int Stack[maxn], depth = 0;
char cmd[20];
int N, M, x, y, z;

int main()
{
#ifdef __AiR_H
    freopen("in.txt", "r", stdin);
#endif // __AiR_H
    scanf("%d %d", &N, &M);
    tree.Init();
    while (M--) {
        scanf("%s", cmd);
        if (strcmp(cmd, "INSERT") == 0) {
            scanf("%d %d", &x, &y);
            tree.insert(x, y);
        } else if (strcmp(cmd, "DELETE") == 0) {
            scanf("%d %d", &x, &y);
            tree.remove(x, y);
        } else if (strcmp(cmd, "MAKE-SAME") == 0) {
            scanf("%d %d %d", &x, &y, &z);
            tree.Make_Same(x, y, z);
        } else if (strcmp(cmd, "REVERSE") == 0) {
            scanf("%d %d", &x, &y);
            tree.Reverse(x, y);
        } else if (strcmp(cmd, "GET-SUM") == 0) {
            scanf("%d %d", &x, &y);
            printf("%d\n", tree.Get_Sum(x, y));
        } else {
            printf("%d\n", tree.Get_MaxSum(1, tree.size[tree.root]-2));
        }
    }
    return 0;
}

int splay::Get_MaxSum(int pos, int tot)
{
    Splay(find_by_order(root, pos), 0);
    Splay(find_by_order(root, pos+tot+1), root);
    return mx[son[son[root][1]][0]];
}

int splay::Get_Sum(int pos, int tot)
{
    Splay(find_by_order(root, pos), 0);
    Splay(find_by_order(root, pos+tot+1), root);
    return sum[son[son[root][1]][0]];
}

void splay::Reverse(int pos, int tot)
{
    Splay(find_by_order(root, pos), 0);
    Splay(find_by_order(root, pos+tot+1), root);
    Update_Rev(son[son[root][1]][0]);
    Push_Up(son[root][1]);
    Push_Up(root);
}

void splay::Make_Same(int pos, int tot, int c)
{
    Splay(find_by_order(root, pos), 0);
    Splay(find_by_order(root, pos+tot+1), root);
    Update_Same(son[son[root][1]][0], c);
    Push_Up(son[root][1]);
    Push_Up(root);
}

void splay::remove(int pos, int tot)
{
    Splay(find_by_order(root, pos), 0);
    Splay(find_by_order(root, pos+tot+1), root);
    erase(son[son[root][1]][0]);
    pre[son[son[root][1]][0]] = 0;
    son[son[root][1]][0] = 0;
    Push_Up(son[root][1]);
    Push_Up(root);
}

void splay::erase(int r)
{
    if (r == 0) {
        return;
    }
    Stack[++depth] = r;
    erase(son[r][0]);
    erase(son[r][1]);
}

void splay::insert(int pos, int tot)
{
    for (int i = 0; i < tot; ++i) {
        scanf("%d", &num[i]);
    }
    Splay(find_by_order(root, pos+1), 0);
    Splay(find_by_order(root, pos+2), root);
    Build(son[son[root][1]][0], 0, tot-1, son[root][1]);
    Push_Up(son[root][1]);
    Push_Up(root);
}

void splay::Splay(int r, int goal)
{
    Push_Down(r);
    while (pre[r] != goal) {
        if (pre[pre[r]] == goal) {
            Push_Down(pre[r]);
            Push_Down(r);
            Rotate(r, son[pre[r]][0] == r);
        } else {
            Push_Down(pre[pre[r]]);
            Push_Down(pre[r]);
            Push_Down(r);
            int b = pre[r];
            int kind = son[pre[b]][0] == b;
            if (son[b][kind] == r) {
                Rotate(r, !kind);
                Rotate(r, kind);
            } else {
                Rotate(b, kind);
                Rotate(r, kind);
            }
        }
    }
    Push_Up(r);
    if (goal == 0) {
        root = r;
    }
}

void splay::Rotate(int a, int kind)
{
    int b = pre[a];
    Push_Down(b);
    Push_Down(a);
    son[b][!kind] = son[a][kind];
    pre[son[a][kind]] = b;
    if (pre[b]) {
        son[pre[b]][son[pre[b]][1] == b] = a;
    }
    pre[a] = pre[b];
    son[a][kind] = b;
    pre[b] = a;
    Push_Up(b);
}

int splay::find_by_order(int r, int k)
{
    Push_Down(r);
    int lsize = size[son[r][0]];
    if (k <= lsize) {
        return find_by_order(son[r][0], k);
    } else if (k > lsize+1) {
        return find_by_order(son[r][1], k - lsize - 1);
    }
    return r;
}

void splay::Push_Down(int r)
{
    if (same[r]) {
        Update_Same(son[r][0], key[r]);
        Update_Same(son[r][1], key[r]);
        same[r] = 0;
    }
    if (reverse[r]) {
        Update_Rev(son[r][0]);
        Update_Rev(son[r][1]);
        reverse[r] = 0;
    }
}

void splay::Update_Same(int r, int _key)
{
    if (r == 0) {
        return;
    }
    key[r] = _key;
    sum[r] = _key * size[r];
    lx[r] = rx[r] = mx[r] = max(_key, sum[r]);
    same[r] = 1;
}

void splay::Update_Rev(int r)
{
    if (r == 0) {
        return;
    }
    swap(son[r][0], son[r][1]);
    swap(lx[r], rx[r]);
    reverse[r] ^= 1;
}

void splay::New_Node(int &r, int father, int _key)
{
    if (depth) {
        r = Stack[depth--];
    } else {
        r = ++Count;
    }
    pre[r] = father;
    son[r][1] = son[r][0] = 0;
    key[r] = _key;
    sum[r] = _key;
    reverse[r] = same[r] = 0;
    lx[r] = rx[r] = mx[r] = _key;
    size[r] = 1;
}

void splay::Init()
{
    root = Count = depth = 0;
    son[root][0] = son[root][1] = pre[root] = size[root] = 0;
    same[root] = reverse[root] = sum[root] = key[root] = 0;
    lx[root] = rx[root] = mx[root] = -INF;
    New_Node(root, 0, -1);
    New_Node(son[root][1], root, -1);
    for (int i = 0; i < N; ++i) {
        scanf("%d", &num[i]);
    }
    Build(son[son[root][1]][0], 0, N-1, son[root][1]);
    Push_Up(son[root][1]);
    Push_Up(root);
}

void splay::Build(int &a, int low, int high, int father)
{
    if (low > high) {
        return;
    }
    int mid = (low + high) / 2;
    New_Node(a, father, num[mid]);
    Build(son[a][0], low, mid-1, a);
    Build(son[a][1], mid+1, high, a);
    Push_Up(a);
}

void splay::Push_Up(int r)
{
    int lson = son[r][0], rson = son[r][1];
    size[r] = size[lson] + size[rson] + 1;
    sum[r] = sum[lson] + sum[rson] + key[r];
    lx[r] = max(lx[lson], sum[lson] + key[r] + max(0, lx[rson]));
    rx[r] = max(rx[rson], sum[rson] + key[r] + max(0, rx[lson]));
    mx[r] = max(0, rx[lson]) + key[r] + max(0, lx[rson]);
    mx[r] = max(mx[r], max(mx[lson], mx[rson]));
}


splay 題集:

http://www.cnblogs.com/kane0526/archive/2012/12/31/2840757.html

http://blog.csdn.net/acm_cxlove/article/details/7815019




Tags: 二叉樹 相關性 而且 記錄 接口

文章來源:


ads
ads

相關文章
ads

相關文章

ad