bzoj3685题解(普通van Emde Boas树)

来源:互联网 发布:麦田的守望者 知乎 编辑:程序博客网 时间:2024/05/16 07:26

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;}
0 0