[bzoj/luogu] 普通平衡樹/文藝平衡樹/二逼平衡樹

普通平衡樹

題目鏈接:
luogu3369
bzoj3224

解題思路

平衡樹模板題,我分別用了 [非旋Treap] 和 [Splay] AC了本題。

一、Splay

每個節點表示一個值,同時記錄該點及其子樹大小、該點表示的值的出現次數、左右兒子、父節點。

  • 插入:將x前驅旋至根,x後繼旋至根的右兒子,那麼根的左兒子的右兒子即爲要插入的位置,如果此位置上無數,則新建一個節點;否則該位置的出現次數和大小加1。
    • 注意:爲了避免找不到x前驅和後繼,應事先插入一個值爲-INF和值爲INF的節點。
  • 刪除:將x前驅旋至根,x後繼旋至根的右兒子,那麼根的左兒子的右兒子即爲要刪除的節點,如果此節點大小爲1,直接刪除;否則該位置的出現次數和大小減1。
  • 查x排名:將x旋至根,則x排名爲根的左兒子大小+1
  • 查排名爲x的數:從根向下查找,如果當前節點的左兒子大小+1=x,則返回當前節點的值;否則,如果當前節點的左兒子大小 x,則向其右兒子查找;否則,向其左兒子查找。
  • 求x前驅:從根向下查找,如果當前節點的值小於等於x,更新ans並向其右兒子查找;否則,向其左兒子查找。更新時,不斷取max即可。
  • 求x後繼:從根向下查找,如果當前節點的值大於等於x,更新ans並向其左兒子查找;否則,向其右兒子查找。更新時,不斷取min即可。

時間複雜度:每次操作 O(log2n)

Code#1

#include<cstdio>
#include<algorithm>

using namespace std;

const int INF = 0x7fffffff;
const int N = 100005;
int n, opt, q;

int cnt = 0, root = 0;

struct Splay_tree{
    int fa, son[2], size, val, times;
}tr[N];

inline void pushup(int x){
    if(x){
        tr[x].size = tr[x].times;
        if(tr[x].son[0])    tr[x].size += tr[tr[x].son[0]].size;
        if(tr[x].son[1])    tr[x].size += tr[tr[x].son[1]].size;
    }
}

inline void rotate(int x, int kind){
    int y = tr[x].fa, z = tr[y].fa, A = tr[y].son[kind], B = tr[x].son[kind], C = tr[x].son[!kind];
    tr[x].son[kind] = y, tr[x].fa = z;
    tr[y].son[!kind] = B, tr[y].fa = x, tr[B].fa = y;
    tr[z].son[tr[z].son[1] == y] = x;
    pushup(y), pushup(x);
}

inline void splay(int x, int goal){
    if(x == goal)   return;
    while(tr[x].fa != goal){
        int y = tr[x].fa, z = tr[y].fa;
        int isrson1 = tr[y].son[1] == x, isrson2 = tr[z].son[1] == y;
        if(z == goal)   rotate(x, !isrson1);
        else{
            if(isrson1 == isrson2)  rotate(y, !isrson2);
            else    rotate(x, !isrson1);
            rotate(x, !isrson2);
        }
    }
    if(goal == 0)   root = x;
}

inline int select(int x){
    int now = root;
    while(now){
        if(tr[now].val == x)    break;
        else if(tr[now].val < x)    now = tr[now].son[1];
        else if(tr[now].val > x)    now = tr[now].son[0];
    }
    if(!now)    return -1;
    return now;
}

inline int getPre(int x){
    int now = root, ans = -INF;
    while(now){
        if(tr[now].val < x){
            ans = max(ans, tr[now].val);
            now = tr[now].son[1];
        }
        else    now = tr[now].son[0];
    }
    return ans;
}

inline int getSub(int x){
    int now = root, ans = INF;
    while(now){
        if(tr[now].val > x){
            ans = min(ans, tr[now].val);
            now = tr[now].son[0];
        }
        else    now = tr[now].son[1];
    }
    return ans;
}

inline int getRank(int x){
    int now = root, ans = 0;
    while(now){
        if(tr[now].val == x){
            ans += tr[tr[now].son[0]].size + 1;
            break;
        }
        else if(tr[now].val < x){
            ans += tr[tr[now].son[0]].size + tr[now].times;
            now = tr[now].son[1];
        }
        else    now = tr[now].son[0];
    }
    return ans - 1;
}

inline int newNode(int val, int f){
    ++cnt;
    tr[cnt].val = val;
    tr[cnt].fa = f;
    tr[cnt].son[0] = tr[cnt].son[1] = 0;
    tr[cnt].size = tr[cnt].times = 1;
    return cnt;
}

inline void insert(int x){
    splay(select(getPre(x)), 0);
    splay(select(getSub(x)), root);
    int t = tr[tr[root].son[1]].son[0];
    if(!t)
        tr[tr[root].son[1]].son[0] = newNode(x, tr[root].son[1]);
    else    tr[t].times++, tr[t].size++;
    pushup(tr[root].son[1]);
    pushup(root);
}

inline void del(int x){
    splay(select(getPre(x)), 0);
    splay(select(getSub(x)), root);
    int t = tr[tr[root].son[1]].son[0];
    if(!t || tr[t].times == 0)  return;
    tr[t].times--, tr[t].size--;
    if(tr[t].times == 0)    tr[tr[root].son[1]].son[0] = 0;
    pushup(tr[root].son[1]);
    pushup(root);
}

inline int findRank(int x){
    int now = root;
    while(now){
        if(tr[tr[now].son[0]].size + 1 <= x && x <= tr[tr[now].son[0]].size + tr[now].times)    break;
        else if(tr[tr[now].son[0]].size + 1 > x)
            now = tr[now].son[0];
        else if(tr[tr[now].son[0]].size + tr[now].times < x){
            x -= tr[tr[now].son[0]].size + tr[now].times;
            now = tr[now].son[1];
        }
    }
    return tr[now].val;
}

int main(){
    scanf("%d", &n);
    root = newNode(-INF, 0);
    tr[root].son[1] = newNode(INF, root), pushup(root);
    while(n--){
        scanf("%d%d", &opt, &q);
        if(opt == 1)    insert(q);
        else if(opt == 2)   del(q);
        else if(opt == 3)   printf("%d\n", getRank(q));
        else if(opt == 4)   printf("%d\n", findRank(q+1));
        else if(opt == 5)   printf("%d\n", getPre(q));
        else if(opt == 6)   printf("%d\n", getSub(q));
    }
    return 0;
}

二、非旋Treap

每個節點表示一個值,同時記錄該點及其子樹大小、左右兒子。

  • 插入:從x處split,新建一個值爲x的節點,再將三部分merge起來。(注:從x處分開:x在前一部分,下同)
  • 刪除:從x、x+1處split成三部分(記爲l、t、r),將t的左右兒子merge起來,這樣就刪除了一個節點,再將三部分merge起來。
  • 查x排名:從x-1處split,則x排名爲前一部分的大小+1
  • 查排名爲x的數:同Splay
  • 求x前驅:同Splay
  • 求x後繼:同Splay

時間複雜度:每次操作 O(log2n)

Code#2

#include<cstdio>
#include<algorithm>

using namespace std;

const int INF = 1e9;
const int N = 100005;
int n, opt, q;

struct Treap{
    int val, son[2], size, hp;
}tr[N];

struct OPT_Treap{
    int cnt, root;
    inline int newNode(int val){
        cnt++;
        tr[cnt].val = val;
        tr[cnt].hp = rand();
        tr[cnt].son[0] = tr[cnt].son[1] = 0;
        tr[cnt].size = 1;
        return cnt;
    }
    inline void pushup(int id){
        tr[id].size = 1;
        if(tr[id].son[0])   tr[id].size += tr[tr[id].son[0]].size;
        if(tr[id].son[1])   tr[id].size += tr[tr[id].son[1]].size;
    }
    inline void pushdown(int id){
        return;
    }
    int merge(int a, int b){
        if(a == 0)  return b;
        if(b == 0)  return a;
        if(tr[a].hp <= tr[b].hp){
            pushdown(a);
            tr[a].son[1] = merge(tr[a].son[1], b);
            pushup(a);
            return a;
        }
        else{
            pushdown(b);
            tr[b].son[0] = merge(a, tr[b].son[0]);
            pushup(b);
            return b;
        }
    }
    void split(int id, int k, int &x, int &y){
        if(!id){
            x = 0, y = 0;
            return;
        }
        pushdown(id);
        if(tr[id].val > k)
            y = id, split(tr[id].son[0], k, x, tr[id].son[0]);
        else
            x = id, split(tr[id].son[1], k, tr[id].son[1], y);
        pushup(id);
    }
    inline void insert(int val){
        int l = 0, r = 0;
        split(root, val, l, r);
        int t = newNode(val);
        root = merge(merge(l, t), r);
    }
    inline void del(int val){
        int l = 0, r = 0, t = 0;
        split(root, val - 1, l, t);
        split(t, val, t, r);
        t = merge(tr[t].son[0], tr[t].son[1]);
        root = merge(merge(l, t), r);
    }
    inline int getRank(int x){
        int ans = 0, l = 0, r = 0;
        split(root, x-1, l, r);
        ans = tr[l].size + 1;
        root = merge(l, r);
        return ans;
    }
    inline int getKth(int k){
        int now = root;
        while(now){
            if(tr[tr[now].son[0]].size + 1 == k)    return tr[now].val;
            else if(tr[tr[now].son[0]].size >= k)   now = tr[now].son[0];
            else    k -= (tr[tr[now].son[0]].size + 1), now = tr[now].son[1];
        }
        return -INF;
    }
    inline int getPre(int x){
        int ans = -INF, now = root;
        while(now){
            if(tr[now].val >= x)    now = tr[now].son[0];
            else{
                ans = max(ans, tr[now].val);
                now = tr[now].son[1];
            }
        }
        return ans;
    }
    inline int getSub(int x){
        int ans = INF, now = root;
        while(now){
            if(tr[now].val <= x)    now = tr[now].son[1];
            else{
                ans = min(ans, tr[now].val);
                now = tr[now].son[0];
            }
        }
        return ans;
    }
}BST;

int main(){
    srand(200127);
    scanf("%d", &n);
    BST.root = BST.newNode(INF);
    while(n--){
        scanf("%d%d", &opt, &q);
        if(opt == 1)    BST.insert(q);
        else if(opt == 2)   BST.del(q);
        else if(opt == 3)   printf("%d\n", BST.getRank(q));
        else if(opt == 4)   printf("%d\n", BST.getKth(q));
        else if(opt == 5)   printf("%d\n", BST.getPre(q));
        else if(opt == 6)   printf("%d\n", BST.getSub(q));
    }
    return 0;
}

文藝平衡樹

題目鏈接:
luogu3391
bzoj3223

解題思路

一、Splay

這道題只有區間翻轉操作,線段樹不好維護,只有用平衡樹了。
對於一次[l,r] 的區間翻轉,把l1 旋至根,r+1 旋至根的右兒子,那麼[l,r] 就在根的右兒子的左兒子處了。和線段樹一樣,我們可以將它的左右兒子互換後打上一個翻轉標記(rev ^= 1),之後再pushdown。

Code#3

#include<cstdio>
#include<algorithm>

using namespace std;

const int INF = 0x7fffffff;
const int N = 100005;
int n, m, ql, qr;

int cnt, root;

struct Splay_tree{
    int fa, son[2], size, val;
    bool rev;
}tr[N];

inline void pushup(int x){
    if(x){
        tr[x].size = 1;
        if(tr[x].son[0])    tr[x].size += tr[tr[x].son[0]].size;
        if(tr[x].son[1])    tr[x].size += tr[tr[x].son[1]].size;
    }
}

inline void pushdown(int x){
    if(tr[x].rev){
        if(tr[x].son[0]){
            tr[tr[x].son[0]].rev ^= 1;
            swap(tr[tr[x].son[0]].son[0], tr[tr[x].son[0]].son[1]);
        }
        if(tr[x].son[1]){
            tr[tr[x].son[1]].rev ^= 1;
            swap(tr[tr[x].son[1]].son[0], tr[tr[x].son[1]].son[1]);
        }
        tr[x].rev = 0;
    }
}

inline void rotate(int x, int kind){
    int y = tr[x].fa, z = tr[y].fa, A = tr[y].son[kind], B = tr[x].son[kind], C = tr[x].son[!kind];
    tr[x].son[kind] = y, tr[x].fa = z;
    tr[y].son[!kind] = B, tr[y].fa = x;
    tr[z].son[tr[z].son[1] == y] = x;
    tr[B].fa = y;
    pushup(y), pushup(x);
}

inline void splay(int x, int goal){
    if(x == goal)   return;
    while(tr[x].fa != goal){
        int y = tr[x].fa, z = tr[y].fa;
        pushdown(z), pushdown(y), pushdown(x);
        int isrson1 = tr[y].son[1] == x, isrson2 = tr[z].son[1] == y;
        if(z == goal)   rotate(x, !isrson1);
        else{
            if(isrson1 == isrson2)  rotate(y, !isrson2);
            else    rotate(x, !isrson1);
            rotate(x, !isrson2);
        }
    }
    if(goal == 0)   root = x;
}

inline int newNode(int val, int f){
    cnt++;
    tr[cnt].val = val;
    tr[cnt].fa = f;
    tr[cnt].son[0] = tr[cnt].son[1] = 0;
    tr[cnt].size = 1;
    return cnt;
}

int select(int x){
    int now = root;
    pushdown(now);
    while(tr[tr[now].son[0]].size + 1 != x){
        if(tr[tr[now].son[0]].size + 1 > x) now = tr[now].son[0];
        else{
            x -= tr[tr[now].son[0]].size + 1;
            now = tr[now].son[1];
        }
        pushdown(now);
    }
    return now;
}

inline void reverse(int l, int r){
    splay(select(l-1), 0);
    splay(select(r+1), root);
    int t = tr[tr[root].son[1]].son[0];
    tr[t].rev ^= 1;
    swap(tr[t].son[0], tr[t].son[1]);
}

int build(int l, int r, int f){
    if(l > r)   return 0;
    int mid = (l + r) >> 1, x = ++cnt;
    tr[x].val = mid - 1;
    tr[x].size = 1;
    tr[x].fa = f;
    tr[x].rev = 0;
    tr[x].son[0] = build(l, mid-1, x);
    tr[x].son[1] = build(mid+1, r, x);
    pushup(x);
    return x;
}

void print(int x){
    pushdown(x);
    if(tr[x].son[0])    print(tr[x].son[0]);
    if(tr[x].val >= 1 && tr[x].val <= n)    printf("%d ", tr[x].val);
    if(tr[x].son[1])    print(tr[x].son[1]);
}

int main(){
    scanf("%d%d", &n, &m);
    root = build(1, n+2, 0);
    while(m--){
        scanf("%d%d", &ql, &qr);
        reverse(ql+1, qr+1);
    }
    print(root);
    return 0;
}

二、非旋Treap

同上。

Code#4

#include<cstdio>
#include<algorithm>

using namespace std;

const int INF = 1e9;
const int N = 100005;
int n, m, ql, qr;

struct Treap{
    int val, size, son[2], hp;
    bool rev;
}tr[N];

struct OPT_Treap{
    int cnt, root;
    inline int newNode(int val){
        cnt++;
        tr[cnt].val = val;
        tr[cnt].hp = rand();
        tr[cnt].size = 1;
        tr[cnt].son[0] = tr[cnt].son[1] = 0;
        tr[cnt].rev = 0;
        return cnt;
    }
    inline void pushup(int id){
        if(!id) return;
        tr[id].size = 1;
        if(tr[id].son[0])   tr[id].size += tr[tr[id].son[0]].size;
        if(tr[id].son[1])   tr[id].size += tr[tr[id].son[1]].size;
    }
    inline void pushdown(int id){
        if(!tr[id].rev) return;
        if(tr[id].son[0]){
            int t = tr[id].son[0];
            tr[t].rev ^= 1;
            swap(tr[t].son[0], tr[t].son[1]);
        }
        if(tr[id].son[1]){
            int t = tr[id].son[1];
            tr[t].rev ^= 1;
            swap(tr[t].son[0], tr[t].son[1]);
        }
        tr[id].rev ^= 1;
    }
    int merge(int a, int b){
        if(a == 0)  return b;
        if(b == 0)  return a;
        if(tr[a].hp <= tr[b].hp){
            pushdown(a);
            tr[a].son[1] = merge(tr[a].son[1], b);
            pushup(a);
            return a;
        }
        else{
            pushdown(b);
            tr[b].son[0] = merge(a, tr[b].son[0]);
            pushup(b);
            return b;
        }
    }
    void split(int id, int k, int &x, int &y){
        if(!id){
            x = 0, y = 0;
            return;
        }
        pushdown(id);
        if(tr[tr[id].son[0]].size >= k)
            y = id, split(tr[id].son[0], k, x, tr[id].son[0]);
        else
            x = id, split(tr[id].son[1], k - tr[tr[id].son[0]].size - 1, tr[id].son[1], y);
        pushup(id);
    }
    inline void reverse(int l, int r){
        int L, t, R;
        split(root, l - 1, L, t);
        split(t, r - l + 1, t, R);
        tr[t].rev ^= 1;
        swap(tr[t].son[0], tr[t].son[1]);
        root = merge(merge(L, t), R);
    }
    inline int build(int l, int r){
        if(l > r)   return 0;
        int mid = (l + r) >> 1;
        int t = newNode(mid);
        tr[t].son[0] = build(l, mid - 1);
        tr[t].son[1] = build(mid + 1, r);
        pushup(t);
        return t;
    }
}BST;

void print(int x){
    BST.pushdown(x);
    if(tr[x].son[0])    print(tr[x].son[0]);
    printf("%d ", tr[x].val);
    if(tr[x].son[1])    print(tr[x].son[1]);
}

int main(){
    srand(200127);
    scanf("%d%d", &n, &m);
    BST.root = BST.build(1, n);
    while(m--){
        scanf("%d%d", &ql, &qr);
        BST.reverse(ql, qr);
    }
    print(BST.root);
    return 0;
}

二逼平衡樹

題目鏈接:
luogu3380
bzoj3196

解題思路

一、線段樹套Splay

這道題與普通平衡樹唯一的不同就在於所有查詢都是區間查詢,那麼我們需要在平衡樹外面套一層線段樹以供區間查詢,即線段樹套平衡樹。
當然,並非真的要在每個線段樹節點內建一顆平衡樹,存一下在這個節點的平衡樹的根的編號就行了。

  • 查詢區間內k的排名:在線段樹上遞歸找查詢的區間,在相應節點上的平衡樹上查詢比k小的數的個數,回溯時將所有答案相加得到了區間內比k小的數的個數,最後+1就是排名;
  • 查詢區間內排名爲k的值:這個要麻煩一點,由於不同線段樹節點上的答案不能進行合併,只能考慮二分答案,問題轉化爲二分出的答案在區間內的排名問題,即第一問;
  • 修改某位置的值:修改即先刪除原值,再插入新值;在線段樹上找到該節點,對所經路線上所有線段樹裏的平衡樹進行刪除插入操作;
  • 查詢k在區間內的前驅:同第一問,只不過在更新答案時不是相加,而是取max;
  • 查詢k在區間內的後繼:同第一問,只不過在更新答案時不是相加,而是取min。

Code#5

#include<cstdio>
#include<iostream>
#include<algorithm>

#define lid id<<1
#define rid id<<1|1
#define mid ((A[id].l+A[id].r)>>1)

using namespace std;

const int INF = 0x7fffffff;
const int N = 50005;
int n, m, a[N], opt, ql, qr, qk, qpos, tmp;

struct splay{
    int size, times, val, son[2], fa;
}B[(int)4e6];

struct segTree{
    int l, r, root;
}A[N<<2];

struct OPT_splay{
    int cnt;
    inline void pushup(int x){
        if(x){
            B[x].size = B[x].times;
            if(B[x].son[0]) B[x].size += B[B[x].son[0]].size;
            if(B[x].son[1]) B[x].size += B[B[x].son[1]].size;
        }
    }
    inline void rotate(int x, int kind){
        int y = B[x].fa, z = B[y].fa, a = B[y].son[kind], b = B[x].son[kind], c = B[x].son[!kind];
        B[x].fa = z, B[x].son[kind] = y;
        B[y].fa = x, B[y].son[!kind] = b;
        B[z].son[B[z].son[1] == y] = x;
        B[b].fa = y;
        pushup(y), pushup(x);
    }
    inline void splay(int x, int goal, int id){
        if(x == goal)   return;
        while(B[x].fa != goal){
            int y = B[x].fa, z = B[y].fa;
            int isrson1 = B[y].son[1] == x, isrson2 = B[z].son[1] == y;
            if(z == goal)   rotate(x, !isrson1);
            else{
                if(isrson1 == isrson2)  rotate(y, !isrson2);
                else    rotate(x, !isrson1);
                rotate(x, !isrson2);
            }
        }
        if(goal == 0)   A[id].root = x;
    }
    inline int newNode(int val, int fa){
        cnt++;
        B[cnt].fa = fa;
        B[cnt].val = val;
        B[cnt].size = B[cnt].times = 1;
        B[cnt].son[0] = B[cnt].son[1] = 0;
        return cnt;
    }
    inline int getPre(int x, int id){
        int now = A[id].root, res = -INF;
        while(now){
            if(B[now].val < x){
                res = max(res, B[now].val);
                now = B[now].son[1];
            }
            else    now = B[now].son[0];
        }
        return res;
    }
    inline int getSub(int x, int id){
        int now = A[id].root, res = INF;
        while(now){
            if(B[now].val > x){
                res = min(res, B[now].val);
                now = B[now].son[0];
            }
            else    now = B[now].son[1];
        }
        return res;
    }
    inline int select(int x, int id){
        int now = A[id].root;
        while(now){
            if(B[now].val == x) break;
            else if(B[now].val > x) now = B[now].son[0];
            else if(B[now].val < x) now = B[now].son[1];
        }
        if(!now)    return -1;
        return now;
    }
    inline int getRank(int x, int id){
        if(select(x, id) != -1) splay(select(x, id), 0, id);
        else    splay(select(getSub(x, id), id), 0, id);
        return B[B[A[id].root].son[0]].size;
    }
    inline void insert(int val, int id){
        splay(select(getPre(val, id), id), 0, id);
        splay(select(getSub(val, id), id), A[id].root, id);
        int t = B[B[A[id].root].son[1]].son[0];
        if(!t)  B[B[A[id].root].son[1]].son[0] = newNode(val, B[A[id].root].son[1]);
        else    B[t].times++, B[t].size++;
        pushup(B[A[id].root].son[1]);
        pushup(A[id].root);
    }
    inline void del(int val, int id){
        splay(select(getPre(val, id), id), 0, id);
        splay(select(getSub(val, id), id), A[id].root, id);
        int t = B[B[A[id].root].son[1]].son[0];
        if(!t || B[t].times == 0)   return;
        B[t].times--, B[t].size--;
        if(B[t].times == 0) B[B[A[id].root].son[1]].son[0] = 0;
        pushup(B[A[id].root].son[1]);
        pushup(A[id].root);
    }
}Splay;

struct OPT_segTree{
    void build(int id, int l, int r){
        A[id].root = Splay.newNode(-INF, 0);
        B[A[id].root].son[1] = Splay.newNode(INF, A[id].root);
        A[id].l = l, A[id].r = r;
        if(A[id].l == A[id].r)  return;
        build(lid, l, mid);
        build(rid, mid+1, r);
    }
    void insert(int id, int pos, int val){
        Splay.insert(val, id);
        if(A[id].l == A[id].r)  return;
        if(pos <= mid)  insert(lid, pos, val);
        else    insert(rid, pos, val);
    }
    int getRank(int id, int l, int r, int x){
        if(A[id].l == l && A[id].r == r)    return Splay.getRank(x, id) - 1;
        if(r <= mid)    return getRank(lid, l, r, x);
        else if(l > mid)    return getRank(rid, l, r, x);
        else    return getRank(lid, l, mid, x) + getRank(rid, mid+1, r, x);
    }
    int getKth(int l, int r, int k){
        int ans = -1, L = 0, R = 1e8;
        while(L <= R){
            int Mid = (L + R) >> 1;
            int t1 = getRank(1, l, r, Mid) + 1;
            int t2 = getRank(1, l, r, Mid+1);
            if(t1 <= k && k <= t2){ ans = Mid; break; }
            if(t2 < k)  L = Mid+1;
            else if(t1 > k) R = Mid-1;
        }
        return ans;
    }
    void modify(int id, int pos, int val){
        Splay.del(a[pos], id);
        Splay.insert(val, id);
        if(A[id].l == A[id].r)  return;
        if(pos <= mid)  modify(lid, pos, val);
        else    modify(rid, pos, val);
    }
    int getPre(int id, int l, int r, int x){
        if(A[id].l == l && A[id].r == r)    return Splay.getPre(x, id);
        if(r <= mid)    return getPre(lid, l, r, x);
        else if(l > mid)    return getPre(rid, l, r, x);
        else    return max(getPre(lid, l, mid, x), getPre(rid, mid+1, r, x));
    }
    int getSub(int id, int l, int r, int x){
        if(A[id].l == l && A[id].r == r)    return Splay.getSub(x, id);
        if(r <= mid)    return getSub(lid, l, r, x);
        else if(l > mid)    return getSub(rid, l, r, x);
        else    return min(getSub(lid, l, mid, x), getSub(rid, mid+1, r, x));
    }
}Seg;

int main(){
    scanf("%d%d", &n, &m);
    Seg.build(1, 1, n);
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
        Seg.insert(1, i, a[i]);
    }
    while(m--){
        scanf("%d", &opt);
        if(opt == 3)    scanf("%d%d", &qpos, &qk);
        else    scanf("%d%d%d", &ql, &qr, &qk);
        if(opt == 1)    printf("%d\n", Seg.getRank(1, ql, qr, qk) + 1);
        else if(opt == 2)   printf("%d\n", Seg.getKth(ql, qr, qk));
        else if(opt == 3)   Seg.modify(1, qpos, qk), a[qpos] = qk;
        else if(opt == 4)   printf("%d\n", Seg.getPre(1, ql, qr, qk));
        else if(opt == 5)   printf("%d\n", Seg.getSub(1, ql, qr, qk));
    }
    return 0;
}

二、線段樹套非旋Treap

同上。

Code#6

#include<cstdio>
#include<algorithm>

#define lid id<<1
#define rid id<<1|1
#define mid ((A[id].l + A[id].r) >> 1)

using namespace std;

const int INF = 0x7fffffff;
const int N = 50005;
int n, m, opt, ql, qr, qk, qpos, a[N];

struct Treap{
    int val, son[2], size, hp;
}B[(int)4e6];
struct segTree{
    int l, r, root;
}A[N<<2];

struct OPT_Treap{
    int cnt;
    inline int newNode(int val){
        cnt++;
        B[cnt].val = val;
        B[cnt].son[0] = B[cnt].son[1] = 0;
        B[cnt].size = 1;
        B[cnt].hp = rand();
        return cnt;
    }
    inline void pushup(int id){
        if(!id) return;
        B[id].size = 1;
        if(B[id].son[0])    B[id].size += B[B[id].son[0]].size;
        if(B[id].son[1])    B[id].size += B[B[id].son[1]].size;
    }
    int merge(int a, int b){
        if(a == 0)  return b;
        if(b == 0)  return a;
        if(B[a].hp <= B[b].hp){
            B[a].son[1] = merge(B[a].son[1], b);
            pushup(a);
            return a;
        }
        else{
            B[b].son[0] = merge(a, B[b].son[0]);
            pushup(b);
            return b;
        }
    }
    void split(int id, int k, int &x, int &y){
        if(!id){
            x = 0, y = 0;
            return;
        }
        if(B[id].val > k)
            y = id, split(B[id].son[0], k, x, B[id].son[0]);
        else
            x = id, split(B[id].son[1], k, B[id].son[1], y);
        pushup(id);
    }
    inline void insert(int &rt, int val){
        int l = 0, r = 0;
        split(rt, val, l, r);
        int t = newNode(val);
        rt = merge(merge(l, t), r);
    }
    inline void del(int &rt, int val){
        int l = 0, r = 0, t = 0;
        split(rt, val - 1, l, t);
        split(t, val, t, r);
        t = merge(B[t].son[0], B[t].son[1]);
        rt = merge(merge(l, t), r);
    }
    inline int getRank(int &rt, int x){
        int l = 0, r = 0;
        split(rt, x - 1, l, r);
        int ans = B[l].size + 1;
        rt = merge(l, r);
        return ans;
    }
    inline int getPre(int &rt, int x){
        int now = rt, ans = -INF;
        while(now){
            if(B[now].val < x){
                ans = max(ans, B[now].val);
                now = B[now].son[1];
            }
            else    now = B[now].son[0];
        }
        return ans;
    }
    inline int getSub(int &rt, int x){
        int now = rt, ans = INF;
        while(now){
            if(B[now].val > x){
                ans = min(ans, B[now].val);
                now = B[now].son[0];
            }
            else    now = B[now].son[1];
        }
        return ans;
    }
}BST;

struct OPT_segTree{
    void build(int id, int l, int r){
        A[id].l = l, A[id].r = r;
        A[id].root = BST.newNode(INF);
        if(A[id].l == A[id].r)  return;
        build(lid, l, mid);
        build(rid, mid+1, r);
    }
    void insert(int id, int pos, int val){
        BST.insert(A[id].root, val);
        if(A[id].l == A[id].r)  return;
        if(pos <= mid)  insert(lid, pos, val);
        else    insert(rid, pos, val);
    }
    void modify(int id, int pos, int val){
        BST.del(A[id].root, a[pos]);
        BST.insert(A[id].root, val);
        if(A[id].l == A[id].r)  return;
        if(pos <= mid)  modify(lid, pos, val);
        else    modify(rid, pos, val);
    }
    int query(int id, int l, int r, int x, int kind){
        if(A[id].l == l && A[id].r == r){
            if(kind == 0)   return BST.getRank(A[id].root, x);
            if(kind == 1)   return BST.getPre(A[id].root, x);
            if(kind == 2)   return BST.getSub(A[id].root, x);
        }
        if(r <= mid)    return query(lid, l, r, x, kind);
        else if(l > mid)    return query(rid, l, r, x, kind);
        else{
            if(kind == 0)   return query(lid, l, mid, x, kind) + query(rid, mid+1, r, x, kind) - 1;
            if(kind == 1)   return max(query(lid, l, mid, x, kind), query(rid, mid+1, r, x, kind));
            if(kind == 2)   return min(query(lid, l, mid, x, kind), query(rid, mid+1, r, x, kind));
        }
    }
    int getKth(int l, int r, int k){
        int L = 0, R = 1e8, ans = 0;
        while(L <= R){
            int Mid = (L + R) >> 1;
            int t1 = query(1, l, r, Mid, 0);
            int t2 = query(1, l, r, Mid+1, 0) - 1;
            if(t1 <= k && k <= t2){ ans = Mid; break; }
            else if(t2 < k) L = Mid + 1;
            else if(t1 > k) R = Mid - 1;
        }
        return ans;
    }
}Seg;

int main(){
    srand(200127);
    scanf("%d%d", &n, &m);
    BST.cnt = 0;
    Seg.build(1, 1, n);
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
        Seg.insert(1, i, a[i]);
    }
    while(m--){
        scanf("%d", &opt);
        if(opt == 3)    scanf("%d%d", &qpos, &qk);
        else    scanf("%d%d%d", &ql, &qr, &qk);
        if(opt == 1)    printf("%d\n", Seg.query(1, ql, qr, qk, 0));
        else if(opt == 2)   printf("%d\n", Seg.getKth(ql, qr, qk));
        else if(opt == 3)   Seg.modify(1, qpos, qk), a[qpos] = qk;
        else if(opt == 4)   printf("%d\n", Seg.query(1, ql, qr, qk, 1));
        else if(opt == 5)   printf("%d\n", Seg.query(1, ql, qr, qk, 2));
    }
    return 0;
}

三、樹狀數組套值域線段樹(帶修改主席樹)

hmm…這道題其實可以不用平衡樹做,因爲要求第k大,自然而然想到主席樹可以做到,但這道題有修改操作,普通的維護前綴和的主席樹修改一次就要把後面所有樹都改了,所以修改一次的時間複雜度就是O(NlogN) 的,顯然不行。於是,帶修改主席樹應運而生:我們不再讓值域線段樹們維護前綴和了,而是讓它們維護樹狀數組上對應的約logN 個點,這樣一次修改的時間複雜度就降到了O(log22N)

  • 查詢區間內k的排名:相當於找比k小的數有多少個(答案是個數+1)。在值域線段樹上二分查找k時,如果往右兒子走,就把左兒子大小加進答案裏去就行了;
  • 查詢區間內排名爲k的值:找到樹狀數組裏面相關的值域線段樹(存進一個數組,見代碼中的A[]和B[]),算出當前點左兒子大小,再決定是向左還是向右二分下去;
  • 修改某位置的值:修改即先刪除原值,再插入新值;找到樹狀數組裏面相關的值域線段樹,對每棵樹都進行刪除和插入操作;
  • 查詢k在區間內的前驅:查詢區間內比k小的數有多少個,如果沒有,輸出-INF;否則輸出區間內相應排名的值;
  • 查詢k在區間內的後繼:查詢區間內比k大的數有多少個,如果沒有,輸出INF;否則輸出區間內相應排名的值。

涉及到值域線段樹一般都要離散化,以保證空間;同時,這道題還必須動態開點才能保證空間。

Code#7

紀念我的第一份超過200行的代碼…

#include<cstdio>
#include<algorithm>

using namespace std;

const int INF = 0x7fffffff;
const int N = 50005;
int n, m, a[N], t[N<<1], f[N<<1], MX, A[20], B[20];//因爲有詢問操作,t[]和f[]空間一定要開夠!
int root[N], cnt;

struct Query{
    int opt, l, r, k, pos;
}q[N];

struct segTree{
    int size, son[2];
}tr[N*15*15];

inline void readin(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]), t[++t[0]] = a[i];
    for(int i = 1; i <= m; i++){
        scanf("%d", &q[i].opt);
        if(q[i].opt != 3){
            scanf("%d%d%d", &q[i].l, &q[i].r, &q[i].k);
            if(q[i].opt != 2)   t[++t[0]] = q[i].k;
        }
        else{
            scanf("%d%d", &q[i].pos, &q[i].k);
            t[++t[0]] = q[i].k;
        }
    }
}
inline void disc(){
    sort(t+1, t+t[0]+1);
    int len = unique(t+1, t+t[0]+1) - (t+1);
    for(int i = 1; i <= n; i++){
        int temp = lower_bound(t+1, t+len+1, a[i]) - t;
        f[temp] = a[i], a[i] = temp;
        MX = max(MX, temp);
    }
    for(int i = 1; i <= m; i++){
        if(q[i].opt == 2)   continue;
        int temp = lower_bound(t+1, t+len+1, q[i].k) - t;
        f[temp] = q[i].k, q[i].k = temp;
        MX = max(MX, temp);
    }
    f[MX+1] = -INF;
    f[MX+2] = INF;
}
inline int lowbit(int x){ return x & -x; }

inline void init1(int x, int X[]){
    X[0] = 0;
    for(int i = x; i; i -= lowbit(i)){
        if(!root[i])    root[i] = ++cnt;
        X[++X[0]] = root[i];
    }
}
inline void init2(int x, int X[]){
    X[0] = 0;
    for(int i = x; i <= n; i += lowbit(i)){
        if(!root[i])    root[i] = ++cnt;
        X[++X[0]] = root[i];
    }
}

inline void pushup(int id){
    tr[id].size = tr[tr[id].son[0]].size + tr[tr[id].son[1]].size;
}

void insert(int &id, int l, int r, int val){
    if(!id) id = ++cnt;
    if(l == r){
        tr[id].size++;
        return;
    }
    int mid = (l + r) >> 1;
    if(val <= mid)  insert(tr[id].son[0], l, mid, val);
    else    insert(tr[id].son[1], mid+1, r, val);
    pushup(id);
}

void del(int &id, int l, int r, int val){
    if(!id) id = ++cnt;
    if(l == r){
        if(tr[id].size > 0) tr[id].size--;
        return;
    }
    int mid = (l + r) >> 1;
    if(val <= mid)  del(tr[id].son[0], l, mid, val);
    else    del(tr[id].son[1], mid+1, r, val);
    pushup(id);
}

int getSmaller(int l, int r, int k){
    if(l == r)  return 0;
    int mid = (l + r) >> 1;
    if(k <= mid){
        for(int i = 1; i <= A[0]; i++){
            if(!tr[A[i]].son[0])    tr[A[i]].son[0] = ++cnt;
            A[i] = tr[A[i]].son[0];
        }
        for(int i = 1; i <= B[0]; i++){
            if(!tr[B[i]].son[0])    tr[B[i]].son[0] = ++cnt;
            B[i] = tr[B[i]].son[0];
        }
        return getSmaller(l, mid, k);
    }
    else{
        int res = 0;
        for(int i = 1; i <= A[0]; i++){
            if(!tr[A[i]].son[1])    tr[A[i]].son[1] = ++cnt;
            res -= tr[tr[A[i]].son[0]].size;
            A[i] = tr[A[i]].son[1];
        }
        for(int i = 1; i <= B[0]; i++){
            if(!tr[B[i]].son[1])    tr[B[i]].son[1] = ++cnt;
            res += tr[tr[B[i]].son[0]].size;
            B[i] = tr[B[i]].son[1];
        }
        return res + getSmaller(mid+1, r, k);
    }
}

int getBigger(int l, int r, int k){
    if(l == r)  return 0;
    int mid = (l + r) >> 1;
    if(k <= mid){
        int res = 0;
        for(int i = 1; i <= A[0]; i++){
            if(!tr[A[i]].son[0])    tr[A[i]].son[0] = ++cnt;
            res -= tr[tr[A[i]].son[1]].size;
            A[i] = tr[A[i]].son[0];
        }
        for(int i = 1; i <= B[0]; i++){
            if(!tr[B[i]].son[0])    tr[B[i]].son[0] = ++cnt;
            res += tr[tr[B[i]].son[1]].size;
            B[i] = tr[B[i]].son[0];
        }
        return res + getBigger(l, mid, k);
    }
    else{
        for(int i = 1; i <= A[0]; i++){
            if(!tr[A[i]].son[1])    tr[A[i]].son[1] = ++cnt;
            A[i] = tr[A[i]].son[1];
        }
        for(int i = 1; i <= B[0]; i++){
            if(!tr[B[i]].son[1])    tr[B[i]].son[1] = ++cnt;
            B[i] = tr[B[i]].son[1];
        }
        return getBigger(mid+1, r, k);
    }
}

int getKth(int l, int r, int k){
    if(l == r)  return l;
    int lsize = 0;
    for(int i = 1; i <= A[0]; i++)  lsize -= tr[tr[A[i]].son[0]].size;
    for(int i = 1; i <= B[0]; i++)  lsize += tr[tr[B[i]].son[0]].size;
    int mid = (l + r) >> 1;
    if(lsize >= k){
        for(int i = 1; i <= A[0]; i++){
            if(!tr[A[i]].son[0])    tr[A[i]].son[0] = ++cnt;
            A[i] = tr[A[i]].son[0];
        }
        for(int i = 1; i <= B[0]; i++){
            if(!tr[B[i]].son[0])    tr[B[i]].son[0] = ++cnt;
            B[i] = tr[B[i]].son[0];
        }
        return getKth(l, mid, k);
    }
    else{
        for(int i = 1; i <= A[0]; i++){
            if(!tr[A[i]].son[1])    tr[A[i]].son[1] = ++cnt;
            A[i] = tr[A[i]].son[1];
        }
        for(int i = 1; i <= B[0]; i++){
            if(!tr[B[i]].son[1])    tr[B[i]].son[1] = ++cnt;
            B[i] = tr[B[i]].son[1];
        }
        return getKth(mid+1, r, k - lsize);
    }
}

inline int getPre(int ql, int qr, int k){
    init1(ql-1, A), init1(qr, B);
    int rank = getSmaller(1, MX, k) + 1;
    init1(ql-1, A), init1(qr, B);
    if(rank == 1)   return MX+1;
    else    return getKth(1, MX, rank-1);
}

inline int getSub(int ql, int qr, int k){
    init1(ql-1, A), init1(qr, B);
    int rank = getBigger(1, MX, k) + 1;
    init1(ql-1, A), init1(qr, B);
    if(rank == 1)   return MX+2;
    else    return getKth(1, MX, qr - ql + 3 - rank);
}

int main(){
    readin();
    disc();
    for(int i = 1; i <= n; i++){
        init2(i, A);
        for(int j = 1; j <= A[0]; j++)
            insert(A[j], 1, MX, a[i]);
    }
    for(int i = 1; i <= m; i++){
        switch(q[i].opt){
            case 1: init1(q[i].l-1, A); init1(q[i].r, B); printf("%d\n", getSmaller(1, MX, q[i].k) + 1); break;
            case 2: init1(q[i].l-1, A); init1(q[i].r, B); printf("%d\n", f[getKth(1, MX, q[i].k)]); break;
            case 3:{
                init2(q[i].pos, A);
                for(int j = 1; j <= A[0]; j++){
                    del(A[j], 1, MX, a[q[i].pos]);
                    insert(A[j], 1, MX, q[i].k);
                }
                a[q[i].pos] = q[i].k;
                break;
            }
            case 4: printf("%d\n", f[getPre(q[i].l, q[i].r, q[i].k)]); break;
            case 5: printf("%d\n", f[getSub(q[i].l, q[i].r, q[i].k)]); break;
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章