bzoj3685题解(普通van Emde Boas树)

Description
设计数据结构支持:
1 x 若x不存在,插入x
2 x 若x存在,删除x
3 输出当前最小值,若不存在输出-1
4 输出当前最大值,若不存在输出-1
5 x 输出x的前驱,若不存在输出-1
6 x 输出x的后继,若不存在输出-1
7 x 若x存在,输出1,否则输出-1

Sample Input
10 11
1 1
1 2
1 3
7 1
7 4
2 1
3
2 3
4
5 3
6 2

Sample Output
1
-1
2
2
2
-1

By ZKY

Solution
几乎要成为黑历史了!调了半个下午。(一定是因为我是个蒟蒻)
和普通平衡树一样,不用upper_bound之类的东西的话平衡树我不会找前趋后继,因为当前结点可能不在平衡树里。我用了zkw线段树。
对于操作1和2,直接找叶子是不是为空,若是则单点修改(+1或-1);(O(logn))
对于操作3,可以从根出发,左边不空就走左边,空了就走右边,直至走到叶子,叶子为空输出-1;(O(logn)
操作4同理,从根出发,右边不空就走右边,空了就走左边,直至走到叶子,叶子为空输出-1;(O(logn))
今天用个正常点的求前趋后继的方法:
如果结点x是x的父亲的右子树(x&1 == 1),那x^1就是x的父亲的左子树;
如果结点x是x的父亲的左子树(x&1 == 0),那x^1就是x的父亲的右子树;
对于操作5,若x是x的父亲的右子树,则x的父亲的左子树中的最大值即为x的前趋。所以当x是x的父亲的左子树时,可以一直找x的父亲(x>>=1),直到x是右子树,实行操作4。若操作4得出-1,则说明x无前趋。若x一直走到根仍不是右子树,则x无前趋。
操作6也同理。(Olog2*n)
操作7,直接找叶子,若为空则输出-1,不为空则输出1;O(1)
总时间复杂度O(n+mlogn),空间复杂度O(n)
/*
Time:7312ms;
Memory:17188KB;
*/

Code:

#include<cstdio>
#include<algorithm>
using namespace std;
int T[4194304], n, m, delta;
inline void add(int p, int d) {
    for(T[p+=delta]+=d, p>>=1; p; p>>=1) 
        T[p] = T[p<<1]+T[(p<<1)+1];
}
inline int querymin(int x) {
    if(T[x] == 0) return -1;
    int iter = x;
    while(iter <= delta) {
        if(T[iter<<1] != 0) iter <<= 1;
        else ++(iter<<=1);
    }
    return iter-delta-1;
}
inline int querymax(int x) {
    if(T[x] == 0) return -1;
    int iter = x;
    while(iter <= delta) {
        if(T[(iter<<1)+1] != 0) ++(iter<<=1);
        else iter<<=1;
    }
    return iter-delta-1;
}
inline int find(int x) {
    if(T[x+delta+1] != 0) return 1;
    else return -1;
}
inline int pred(int x) {
    int pos = x+delta+1;
    while(1) {
        if(pos == 1) return -1;
        if(pos&1 && T[pos^1]) return querymax(pos^1);
        pos>>=1;
    }
    return -1;
}
inline int succ(int x) {
    int pos = x+delta+1;
    while(1) {
        if(pos == 1) return -1;
        if(!(pos&1) && T[pos^1]) return querymin(pos^1);
        pos>>=1;
    }
    return -1;
}
int main() {
    int x, opt;
    scanf("%d%d", &n, &m);
    for(delta = 1; delta < n+1; delta<<=1);
    for(int i = 1; i <= m; ++i) {
        scanf("%d", &opt);
        switch(opt) {
            case 1 :scanf("%d", &x); if(find(x) == -1) 
                    add(x+1, 1); break;
            case 2 :scanf("%d", &x); if(find(x) == 1) 
                    add(x+1, -1); break;
            case 3 :printf("%d\n", querymin(1)); break;
            case 4 :printf("%d\n", querymax(1)); break;
            case 5 :scanf("%d", &x); 
                    printf("%d\n", pred(x)); break;
            case 6 :scanf("%d", &x); 
                    printf("%d\n", succ(x)); break;
            case 7 :scanf("%d", &x); 
                    printf("%d\n", find(x)); break;
            default:;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值