题意:
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
- 插入x数
- 删除x数(若有多个相同的数,因只删除一个)
- 查询x数的排名(若有多个相同的数,因输出最小的排名)
- 查询排名为x的数
- 求x的前驱(前驱定义为小于x,且最大的数)
- 求x的后继(后继定义为大于x,且最小的数)
思路:
平衡树还不会,后面补上。先用权值线段树来做。
权值线段树不了解的先看:权值线段树
插入删除操作,用一个更新操作完成,将其对应的出现次数+1或-1即可。
查询数x的排名:转化为找x前面有多少数,然后+1。找x前面有多少数,即在[1, x-1]上线段树求和。
查询第k小的数:找根节点的左孩子有多少数,如果>=k,那么就在左孩子中找,反之在右孩子中找,这样递归下去,到叶子节点就找到了。
查询x的前驱:根节点分为两个区间,【l, mid】和【mid+1, r】,如果x > mid+1并且右孩子非空,那么x的前驱可能在右孩子中,反之在左孩子中。因为前驱是要找比x小的最大数,所以每次优先在右孩子中找,找不到再去左孩子。
上面有个可能,举个例子:假设x = mid+2,而mid+1处是空的(可能被删除了),那么这时候前驱就在左孩子中了。
查询x的后继和前驱类似。
// 权值线段树
#include <cstdio>
#include <cstring>
#include <algorithm>
#define lson l, mid, rt<<1
#define rson mid+1, r, rt<<1|1
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 100000+5;
int Tree[maxn<<2], X[maxn];
struct Operation{
int p, v;
}Op[maxn];
// 更新操作(插入删除)
void update(int k, int l, int r, int rt, int val){
Tree[rt]+= val;
if(l == r) return;
int mid = (l+r) >> 1;
if(k <= mid) update(k, lson, val);
else update(k, rson, val);
}
// 找val的排名: 求[1, val-1]的和
int rank(int la, int rb, int l, int r, int rt){
if(la > r||rb < l) return 0;
if(la <= l&&rb >= r) return Tree[rt];
int mid = (l+r) >> 1, ans = 0;
if(la <= mid) ans+= rank(la, rb, lson);
if(rb > mid) ans+= rank(la, rb, rson);
return ans;
}
// 找第k小
int find_kth(int l, int r, int rt, int k){
if(l == r) return l;
int mid = (l+r) >> 1;
if(Tree[rt<<1] >= k) return find_kth(lson, k);
else return find_kth(rson, k-Tree[rt<<1]);
}
int find_rightest(int l, int r, int rt){
if(l == r) return l;
int mid = (l+r) >> 1;
if(Tree[rt<<1|1]) return find_rightest(rson);
else return find_rightest(lson);
}
// 找val的前驱
int pre(int l, int r, int rt, int val){
if(r < val){
if(Tree[rt]) return find_rightest(l, r, rt);
return 0;
}
int mid = (l+r) >> 1, ans;
if(val > mid+1&&Tree[rt<<1|1]&&(ans = pre(rson, val))) return ans;
return pre(lson, val);
}
int find_leftest(int l, int r, int rt){
if(l == r) return l;
int mid = (l+r) >> 1;
if(Tree[rt<<1]) return find_leftest(lson);
else return find_leftest(rson);
}
// 找val的后继
int nex(int l, int r, int rt, int val){
if(val < l){
if(Tree[rt]) return find_leftest(l, r, rt);
return 0;
}
int mid = (l+r) >> 1, ans;
if(val < mid&&Tree[rt<<1]&&(ans = nex(lson, val))) return ans;
return nex(rson, val);
}
int main()
{
int n;
while(scanf("%d",&n) == 1&&n){
memset(Tree, 0, sizeof(Tree));
int tot = 0;
for(int i = 1; i <= n; ++i){
scanf("%d%d", &Op[i].p,&Op[i].v);
if(Op[i].p == 1) X[++tot] = Op[i].v;
}
//printf("n = %d, tot = %d\n",n,tot);
sort(X+1, X+tot+1);
int p, v, t;
for(int i = 1; i <= n; ++i){
p = Op[i].p;
v = Op[i].v;
int k = lower_bound(X+1, X+tot+1, v) - X;
switch (p){
case 1: update(k, 1, n, 1, 1); break;
case 2: update(k, 1, n, 1, -1); break;
case 3: printf("%d\n", 1+rank(1, k-1, 1, n, 1)); break; // 找x的排名
case 4: t = find_kth(1, n, 1, v); printf("%d\n", X[t]); break;
case 5: t = pre(1, n, 1, k); printf("%d\n", X[t]); break;
case 6: k = upper_bound(X+1, X+tot+1, v) - X;
t = nex(1, n, 1, k-1); printf("%d\n", X[t]);
}
}
}
return 0;
}