题目描述
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
查询在区间内的排名
查询区间内排名为的值
修改某一位值上的数值
查询在区间内的前驱(前驱定义为小于,且最大的数)
查询在区间内的后继(后继定义为大于,且最小的数)
输入格式
第一行两个数 表示长度为的有序序列和个操作
第二行有个数,表示有序序列
下面有行,表示操作标号
若 则为操作,之后有三个数 表示查询在区间的排名
若 则为操作,之后有三个数 表示查询区间内排名为的数
若 则为操作,之后有两个数 表示将位置的数修改为
若 则为操作,之后有三个数 表示查询区间内的前驱
若 则为操作,之后有三个数 表示查询区间内的后继
输出格式
对于操作各输出一行,表示查询结果
样例输入
样例输出
提示
和的数据范围:
序列中每个数的数据范围:
虽然原题没有,但事实上操作的可能为负数
前言:
线段树通常用来解决区间上的问题,而平衡树可以支持查询排名、前驱、后继、第小等问题。
如果要支持动态的区间上的上述问题,就必须用树套树这种神奇的数据结构。(可能还有别的做法吧,但本蒟蒻只会线段树套平衡树)
具体来说,用线段树对原序列进行划分,每个节点上开一颗平衡树,用来维护该段区间元素权值的信息。
举个栗子:
设原序列是
那么将原序列构造一颗线段树,如下图所示
如果要在第~个元素中求的后继,那么我们就可以在中分别求出的后继为,对这些值取即可。
那么求前驱、排名也是类似的
关于如何求第小数,我们可以二分答案, 求最大的,使得在中比小的数的个数为
时空复杂度分析:
- 空间复杂度:
线段树的树高是,而线段树上每一层区间的并集都是,即每一层的空间复杂度是的,所以总复杂度为 - 时间复杂度
对于求前驱、后继和排名,原询问区间会在线段树上划分成段区间,在每段区间中查询信息时间复杂度是的,所以总时间复杂度为
对于求第小,因为需要二分答案,所以在配合对原序列和修改的值离散化的情况下可以做到(当然本人很懒,而且此题对常熟没有太大的要求,就没有离散化)
代码
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 50005;
const int maxq = 10005;
const int maxt = 2000005;
const int oo = 2147483647;
int a[maxn];
int max(int x, int y) {return x > y ? x : y;}
int min(int x, int y) {return x < y ? x : y;}
int read() {
char ch = getchar(); bool f = 1;
while(ch < '0' || ch > '9') f &= ch != '-', ch = getchar();
int res = 0;
while(ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return f ? res : -res;
}
void write(int x) {
if(x < 0) x = -x, putchar('-');
int len = 0, res[15];
for(; x; res[++len] = x % 10, x /= 10);
for(int i = len; i >= 1; i--) putchar(res[i] + 48);
if(!len) putchar('0');
}
class Treap {
private:
static int cnt;
static struct node {int son[2], val, pri, cnt, siz;} t[maxt];
inline int build(int val) {
t[++cnt] = (node) {{0, 0}, val, rand(), 1, 1};
return cnt;
}
inline void update(int p) {t[p].siz = t[t[p].son[0]].siz + t[p].cnt + t[t[p].son[1]].siz;}
void rotate(int &p, bool d) {
int k = t[p].son[d];
t[p].son[d] = t[k].son[d ^ 1];
t[k].son[d ^ 1] = p;
update(p); update(p = k);
}
public:
int rt;
inline void clear() {rt = 0;}
void insert(int &p, int val) {
if(!p) {p = build(val); return;}
t[p].siz++;
if(t[p].val == val) {t[p].cnt++; return;}
bool d = t[p].val < val;
insert(t[p].son[d], val);
if(t[p].pri > t[t[p].son[d]].pri) rotate(p, d);
}
void remove(int &p, int val) {
if(t[p].val == val) {
if(t[p].cnt > 1) {t[p].cnt--; t[p].siz--; return;}
if(!t[p].son[0] || !t[p].son[1]) {p = t[p].son[0] + t[p].son[1]; return;}
bool d = t[t[p].son[0]].pri > t[t[p].son[1]].pri;
rotate(p, d); remove(p, val);
return;
}
t[p].siz--;
bool d = t[p].val < val;
remove(t[p].son[d], val);
}
int lower(int p, int val) {
if(!p) return 0;
if(t[p].val > val) return lower(t[p].son[0], val);
if(t[p].val == val) return t[t[p].son[0]].siz;
return t[t[p].son[0]].siz + t[p].cnt + lower(t[p].son[1], val);
}
int kth(int p, int rnk) {
if(!p) return 0;
if(t[t[p].son[0]].siz >= rnk) return kth(t[p].son[0], rnk);
if(t[t[p].son[0]].siz + t[p].cnt >= rnk) return t[p].val;
return kth(t[p].son[1], rnk - t[t[p].son[0]].siz - t[p].cnt);
}
int pre(int p, int val) {
if(!p) return -oo;
if(t[p].val >= val) return pre(t[p].son[0], val);
return max(t[p].val, pre(t[p].son[1], val));
}
int suc(int p, int val) {
if(!p) return oo;
if(t[p].val <= val) return suc(t[p].son[1], val);
return min(t[p].val, suc(t[p].son[0], val));
}
} sgt[maxn << 2];
int Treap::cnt = 0;
Treap::node Treap::t[maxt];
void build(int p, int l, int r) {
sgt[p].clear();
for(int i = l; i <= r; i++) sgt[p].insert(sgt[p].rt, a[i]);
if(l == r) return;
int mid = l + r >> 1;
build(p + p, l, mid);
build(p + p + 1, mid + 1, r);
}
int query_lower(int p, int l, int r, int x, int y, int k) {
if(l == x && r == y) return sgt[p].lower(sgt[p].rt, k);
int mid = l + r >> 1;
if(y <= mid) return query_lower(p + p, l, mid, x, y, k);
else if(x > mid) return query_lower(p + p + 1, mid + 1, r, x, y, k);
else return query_lower(p + p, l, mid, x, mid, k) + query_lower(p + p + 1, mid + 1, r, mid + 1, y, k);
}
int query_pre(int p, int l, int r, int x, int y, int k) {
if(l == x && r == y) return sgt[p].pre(sgt[p].rt, k);
int mid = l + r >> 1;
if(y <= mid) return query_pre(p + p, l, mid, x, y, k);
else if(x > mid) return query_pre(p + p + 1, mid + 1, r , x, y, k);
else return max(query_pre(p + p, l, mid, x, mid, k), query_pre(p + p + 1, mid + 1, r, mid + 1, y, k));
}
int query_suc(int p, int l, int r, int x, int y, int k) {
if(l == x && r == y) return sgt[p].suc(sgt[p].rt, k);
int mid = l + r >> 1;
if(y <= mid) return query_suc(p + p, l, mid, x, y, k);
else if(x > mid) return query_suc(p + p + 1, mid + 1, r , x, y, k);
else return min(query_suc(p + p, l, mid, x, mid, k), query_suc(p + p + 1, mid + 1, r, mid + 1, y, k));
}
void update(int p, int l, int r, int x, int k) {
sgt[p].remove(sgt[p].rt, a[x]); sgt[p].insert(sgt[p].rt, k);
if(l == r) return;
int mid = l + r >> 1;
if(x <= mid) update(p + p, l, mid, x, k);
else update(p + p + 1, mid + 1, r, x, k);
}
int main() {
int n = read(), q = read();
for(int i = 1; i <= n; i++) a[i] = read();
build(1, 1, n);
while(q--) {
int opt = read();
if(opt == 1) {
int l = read(), r = read(), k = read();
write(query_lower(1, 1, n, l, r, k) + 1), putchar('\n');
}
if(opt == 2) {
int l = read(), r = read(), k = read();
int L = 0, R = 1e8;
while(L < R) {
int mid = L + R + 1 >> 1;
if(query_lower(1, 1, n, l, r, mid) < k) L = mid;
else R = mid - 1;
}
write(R), putchar('\n');
}
if(opt == 3) {
int pos = read(), k = read();
update(1, 1, n, pos, k);
a[pos] = k;
}
if(opt == 4) {
int l = read(), r = read(), k = read();
write(query_pre(1, 1, n, l, r, k)), putchar('\n');
}
if(opt == 5) {
int l = read(), r = read(), k = read();
write(query_suc(1, 1, n, l, r, k)), putchar('\n');
}
}
return 0;
}