先来orzZKW大神几分钟
题目大意:写一个数据结构,支持平衡树的各种操作
真的要用平衡树吗?不,我们要使用时间效率更好的zkw线段树。《统计的力量》教导我们如下做法:
f[i]记录i出现的频数;
插入:++f[i];
删除:–f[i];
询问x排名:求x-1的前缀和,再加一。因为题目要求最靠前的排名嘛;
查询排名:自顶向下,往左走,左边不够往右。往右一定注意减去左区间的前缀和。(手画线段树才得到的教训啊!)直到走到叶子;
前趋后继的比较朴素的方法:找到叶子后直接向前(后)扫;
数据预处理:500w左右的数组记录线段树,结点数设得比200w大,读入数据后要加上100w+1(c++选手),初始化为0即可。
可以用离散化做得更快。
```
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int T[5000010];
const int dt = 1000001;//变成正数
int n, delta;
void add(int x, int d) {
for(T[x+=delta] += d, x>>=1; x; x>>=1)
T[x] = T[x<<1]+T[(x<<1)+1];
}
int query(int t) {
int s = 1, ans = 0;
for(s+=delta-1, t+=delta; s^t^1; s>>=1, t>>=1) {
if(~s&1) ans += T[s^1];
if(t&1) ans += T[t^1];
}
return ans+1;
}
int rank(int x) {
int iter = 1;
while(iter < delta) {
if(x > T[iter<<1]) {
x -= T[iter<<1];
iter = (iter<<1)+1;
}
else iter<<=1;
}
return iter-dt-delta;
}
int pred(int x) {
x += delta-1;
while(!T[x]) --x;
return x-dt-delta;
}
int succ(int x) {
x += delta+1;
while(!T[x]) ++x;
return x-dt-delta;
}
int main() {
int opt, x;
freopen("t.txt", "r", stdin);
scanf("%d", &n);
delta = (1<<21);
for(int i = 1; i <= n; ++i) {
scanf("%d%d", &opt, &x);
switch(opt) {
case 1 :add(x+dt, 1); break;
case 2 :add(x+dt, -1); break;
case 3 :printf("%d\n", query(x+dt));
break;
case 4 :printf("%d\n", rank(x));
break;
case 5 :printf("%d\n", pred(x+dt));
break;
case 6 :printf("%d\n", succ(x+dt));
break;
default:;
}
}
return 0;
}