值域分块模板
template <typename T>
struct Block {
int B, N, tot;
vector<T> cnt, Cnt, L, R, belong;
Block(int n): N(n - 1), B(sqrt(n - 1)), cnt(n), Cnt(n), belong(n), L(n), R(n) {
tot = N / B;
for (int i = 1; i <= N; ++i)
belong[i] = (i - 1) / B + 1;
for (int i = 1; i <= tot; ++i) {
if (i * B > N) break;
L[i] = (i - 1) * B + 1;
R[i] = i * B; //预处理每一块的左右端点
}
if (R[tot] < N) {
tot++, L[tot] = R[tot - 1] + 1, R[tot] = N;
}
}
void add(int x, int k) {
cnt[x] += k;
Cnt[belong[x]] += k;
}
//求前缀
T ask(int x) { //get sum in range[1,x]
T res = 0;
for (int i = 1; i <= tot; i += 1) {
if (x <= R[i]) {
for (int j = L[i]; j <= x; j += 1) {
res += cnt[j];
}
break;
} else {
res += Cnt[i];
}
}
return res;
}
//求排名
T get_rank(int k) { //get rank of k in range[l,r]
T rank = 1;
for (int i = 1; i <= tot; ++i) {
if (k <= R[i]) {//k不如这个块右端点大,那么k一定在这个块中
for (int j = L[i]; j < k; ++j) { //暴力枚举到k,找到比k小的有几个数
rank += cnt[j];
}
} else {
rank += Cnt[i];
}
}
return rank;
}
T get_kth(int k) { //get kth number in range[l,r]
for (int i = 1; i <= tot; ++i) {
if (k <= Cnt[i]) //如果k出现次数不足这个块中的数的数量了,说明第k名在这个块内
for (int j = L[i]; j <= R[i]; ++j) {
if (cnt[j] == 0) continue;
k -= cnt[j];
if (k <= 0) {
return C[j];
}
}
else k -= Cnt[i];
}
return -1;//未找到,返回-1
}
/*
查k的前驱可以先查k的排名rank,然后查排名为rank-1的数的值
查k的后继同上
*/
//查前驱
T get_pre(const int k) {
int rank = get_rank(k);
if (rank == 1) return -2147483647; //k已经是第一名了,不存在前驱
int pre = get_kth(rank - 1); //查排名前一位元素
return pre;
}
//查后驱
T get_back(const int k) {
int rank = get_rank(k);
if (rank == -1) return 2147483647; //k最大,不存在答案
int back = cnt[k] > 0 ? get_kth(rank + 1) : get_kth(rank);
//这里细节一波,因为查的数本身可能出现在序列里,故特判
if (back == -1) return 2147483647; //没查到后继,不存在答案
return back;
}
};
参考文章:

浙公网安备 33010602011771号