BZOJ3196 二逼平衡树
? 解题记录 ? ? BZOJ ? ? Splay ? ? 线段树 ? ? 树套树 ? ? 平衡树 ?    2017-09-17 11:47:24    416    1    0

Description

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
1.查询k在区间内的排名
2.查询区间内排名为k的值
3.修改某一位值上的数值
4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)
5.查询k在区间内的后继(后继定义为大于x,且最小的数)

Input

第一行两个数 n,m 表示长度为n的有序序列和m个操作
第二行有n个数,表示有序序列
下面有m行,opt表示操作标号
若opt=1 则为操作1,之后有三个数l,r,k 表示查询k在区间[l,r]的排名
若opt=2 则为操作2,之后有三个数l,r,k 表示查询区间[l,r]内排名为k的数
若opt=3 则为操作3,之后有两个数pos,k 表示将pos位置的数修改为k
若opt=4 则为操作4,之后有三个数l,r,k 表示查询区间[l,r]内k的前驱
若opt=5 则为操作5,之后有三个数l,r,k 表示查询区间[l,r]内k的后继

Output

对于操作1,2,4,5各输出一行,表示查询结果

Sample Input

9 6
4 2 2 1 9 4 0 1 1
2 1 4 3
3 4 10
2 1 4 3
1 2 5 9
4 3 9 5
5 2 8 5

Sample Output

2
4
3
4
9

HINT

 

1.n和m的数据范围:n,m<=50000


2.序列中每个数的数据范围:[0,1e8]


3.虽然原题没有,但事实上5操作的k可能为负数

 

哇,这个树套树真的是二逼,写完之后我整个人都二逼了啊QAQ。

做这道题前前后后起码花了20个小时,第一次脑补神奇平衡树爆零,第二次树状数组套动态开点线段树10分。一气之下直接上线段树+Splay居然过了。

这里有几点值得注意的:1、Splay的常数不能太大。2、后继查询的k可能有负数。3、线段树下面吊着的平衡树根是会变的,要时刻更新。4、唯一比较棘手的操作是区间K大,这里需要二分查找O(log^3 n)

下面是长出新高度的代码:

#include<cstdio>
#include<algorithm>
#define L 0
#define R 1
using namespace std;
const int maxn = 1e5 + 5;
int n, m, a;

inline void read(int & x) {
    x = 0;
    int f = 1;
    char c = getchar();
    while(c > '9' || c < '0') {
		if(c == '-') f = -1; c = getchar();
	}
    while(c <= '9' && c >= '0') x = x * 10 + c - '0', c = getchar();
    x *= f;
}

namespace Splay {
    struct node {
        int fa, ch[2], num, size, cnt;
    }tree[maxn * 40];
    int cnt;
    void push_up(int rt) {
        if(rt == 0) return;
        int tl = tree[rt].ch[L];
        int tr = tree[rt].ch[R];
        tree[rt].size = tree[tl].size + tree[tr].size + tree[rt].cnt;
    }
    void rotate(int rt) {
        int p = tree[rt].fa;
        int a = tree[p].fa;
        int l = tree[p].ch[R] == rt;
        int r = l ^ 1;
        if(a) tree[a].ch[tree[a].ch[R] == p] = rt;
        tree[rt].fa = a, tree[p].fa = rt, tree[tree[rt].ch[r]].fa = tree[rt].ch[r] ? p : 0;
        tree[p].ch[l] = tree[rt].ch[r], tree[rt].ch[r] = p;
        push_up(p), push_up(rt), push_up(a);
    }
    inline void splay(int x, int y, int & root) {
        int des = tree[y].fa;
        while(tree[x].fa != des) {
            int p = tree[x].fa;
            int a = tree[p].fa;
            if(p != y)
                if((tree[a].ch[R] == p) ^ (tree[p].ch[R] == x)) rotate(x);
                else rotate(p);
            rotate(x);
        }
        if(y == root) root = x;
    }
    int find(int x, int root) {
        int rt = root, last = 0, lm = 0;
        while(tree[rt].num != x && rt != 0) {
            if(tree[rt].num <= x && tree[rt].num > lm)    
                lm = tree[rt].num, last = rt;
            rt = tree[rt].ch[x > tree[rt].num];
        }
        return rt == 0 ? last : rt;
    }
    int qrank(int x, int & root) {
        int rt = find(x, root);
        if(rt) splay(rt, root, root);
        else return 0;
        if(tree[rt].num == x) return tree[tree[rt].ch[L]].size;
        else return tree[tree[rt].ch[L]].size + tree[rt].cnt;
    }
    int qnode(int rt, int & root, bool type) {
        splay(rt, root, root);
        rt = tree[rt].ch[type];
        if(!rt) return -1;
        while(tree[rt].ch[type ^ 1])
            rt = tree[rt].ch[type ^ 1];
        return rt;
    }
    void del(int x, int & root) {
        int rt = find(x, root);
        --tree[rt].cnt, --tree[rt].size;
        if(tree[rt].cnt) {splay(rt, root, root);return;}
        int pr = qnode(rt, root, 0);
        int su = qnode(rt, root, 1);
        if(pr == -1 && su == -1)  {root = 0;return;}
        else if(pr == -1) {splay(su, root, root), tree[su].ch[L] = 0, push_up(su);return;}
        else if(su == -1) {splay(pr, root, root), tree[pr].ch[R] = 0, push_up(pr);return;}
        else {splay(pr, root, root), splay(su, tree[pr].ch[R], root), tree[su].ch[L] = 0;}
        push_up(su), push_up(pr);
    }
    void insert(int now, int x, int last, int & root) {
        if(now == 0) {tree[++cnt] = (node){last, 0, 0, x, 1, 1};
        tree[last].ch[tree[last].num < x] = cnt, splay(cnt, root, root); return;}
        if(tree[now].num == x) {++tree[now].cnt, ++tree[now].size, splay(now, root, root);return;}
        insert(tree[now].ch[tree[now].num < x], x, now, root);
    }
}

namespace Segtree {
    struct node {
        int ch[2], l, r, Spn;
    }tree[maxn << 1];
    int cnt, root;
    void build(int & rt, int l, int r) {
        rt = ++cnt, tree[cnt].l = l, tree[cnt].r = r;
        if(l == r) return;
        int mid = l + r >> 1;
        build(tree[rt].ch[L], l, mid);
        build(tree[rt].ch[R], mid + 1, r);
    }
    //记得换根 
    void add(int rt, int pos, int x) {
        int tl = tree[rt].l, tr = tree[rt].r;
        if(tl == tr) {Splay::insert(tree[rt].Spn, x, 0, tree[rt].Spn);return;}
        int mid = tl + tr >> 1;
        if(pos <= mid) add(tree[rt].ch[L], pos, x);
        else add(tree[rt].ch[R], pos, x);
        Splay::insert(tree[rt].Spn, x, 0, tree[rt].Spn);
    } 
    int del(int rt, int pos) {
        int tl = tree[rt].l, tr = tree[rt].r;
        if(tl == tr) {
            int ans = Splay::tree[tree[rt].Spn].num;
            Splay::del(Splay::tree[tree[rt].Spn].num, tree[rt].Spn);
            return ans;
        }
        int mid = tl + tr >> 1, ans;
        if(pos <= mid) ans = del(tree[rt].ch[L], pos);
        else ans = del(tree[rt].ch[R], pos);
        Splay::del(ans, tree[rt].Spn);
        return ans;
    }
    void edit(int rt, int pos, int x) {
        del(rt, pos);
        add(rt, pos, x);
    }
    int qrank(int rt, int l, int r, int k) {
        int tl = tree[rt].l, tr = tree[rt].r;
        if(tl == l && tr == r) return Splay::qrank(k, tree[rt].Spn);
        int mid = tl + tr >> 1;
        if(r <= mid) return qrank(tree[rt].ch[L], l, r, k);
        else if(l > mid) return qrank(tree[rt].ch[R], l, r, k);
        else return qrank(tree[rt].ch[L], l, mid, k) + qrank(tree[rt].ch[R], mid + 1, r, k);
    }
    int qnum(int l, int r, int k) {
        int al = 0, ar = 1e8 + 1;
        while(al + 1 < ar) {
            int mid = al + ar >> 1;
            int ans = qrank(1, l, r, mid) + 1;
            if(ans > k) ar = mid;
            else al = mid; 
        }
        return al;
    }
    int qnode(int rt, int l, int r, int k, bool type) {
         int tl = tree[rt].l, tr = tree[rt].r;
        if(tl == l && tr == r) {
            Splay::insert(tree[rt].Spn, k, 0, tree[rt].Spn);
            int ans = Splay::qnode(tree[rt].Spn, tree[rt].Spn, type);
            Splay::del(k, tree[rt].Spn);
            return ans == -1 ? -1 : Splay::tree[ans].num;
         }
        int mid = tl + tr >> 1;
        if(r <= mid) return qnode(tree[rt].ch[L], l, r, k, type);
        else if(l > mid) return qnode(tree[rt].ch[R], l, r, k, type);
        else {
            int ans1 = qnode(tree[rt].ch[L], l, mid, k, type);
            int ans2 = qnode(tree[rt].ch[R], mid + 1, r, k, type);
            if(type) {
                if(ans1 == -1) return ans2 == -1 ? -1 : ans2;
                if(ans2 == -1) return ans1;
                else return min(ans2, ans1);
            }else 
            return max(ans2, ans1);
        }
    }
}

int main() {
    read(n), read(m);
    Segtree::build(Segtree::root, 1, n);
    for(register int i = 1; i <= n; ++i) read(a), Segtree::add(1, i, a);
    int opt, a, b, c;
    while(m--) {
        read(opt);
        if(opt != 3) read(a), read(b), read(c);
        else read(a), read(b);
        if(opt == 1) printf("%d\n", Segtree::qrank(1, a, b, c) + 1);
        if(opt == 2) printf("%d\n", Segtree::qnum(a, b, c));
        if(opt == 3) Segtree::edit(1, a, b);
        if(opt == 4) {
            int ans = Segtree::qnode(1, a, b, c, 0);
            printf("%d\n", ans == -1 ? -2147483647 : ans);
        }
        if(opt == 5) {
            int ans = Segtree::qnode(1, a, b, c, 1);
            printf("%d\n", ans == -1 ? 2147483647 : ans);
        }
    }
    return 0;
} 

 

上一篇: BZOJ3295 动态逆序对

下一篇: 洛谷P3370 【模板】字符串哈希

416 人读过
立即登录, 发表评论.
没有帐号? 立即注册
0 条评论
文档导航