题目描述
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:
1.1.1.查询kkk在区间内的排名
2.2.2.查询区间内排名为kkk的值
3.3.3.修改某一位值上的数值
4.4.4.查询kkk在区间内的前驱(前驱定义为小于xxx,且最大的数)
5.5.5.查询kkk在区间内的后继(后继定义为大于xxx,且最小的数)
输入格式
第一行两个数 n,mn,mn,m 表示长度为nnn的有序序列和mmm个操作
第二行有nnn个数,表示有序序列
下面有mmm行,optoptopt表示操作标号
若opt=1opt=1opt=1 则为操作111,之后有三个数l,r,kl,r,kl,r,k 表示查询kkk在区间[l,r][l,r][l,r]的排名
若opt=2opt=2opt=2 则为操作222,之后有三个数l,r,kl,r,kl,r,k 表示查询区间[l,r][l,r][l,r]内排名为kkk的数
若opt=3opt=3opt=3 则为操作333,之后有两个数pos,kpos,kpos,k 表示将pospospos位置的数修改为kkk
若opt=4opt=4opt=4 则为操作444,之后有三个数l,r,kl,r,kl,r,k 表示查询区间[l,r][l,r][l,r]内kkk的前驱
若opt=5opt=5opt=5 则为操作555,之后有三个数l,r,kl,r,kl,r,k 表示查询区间[l,r][l,r][l,r]内kkk的后继
输出格式
对于操作1,2,4,51,2,4,51,2,4,5各输出一行,表示查询结果
样例输入
666
444 222 222 111 999 444 000 111 111
222 111 444 333
333 444 101010
222 111 444 333
111 222 555 999
444 333 999 555
555 222 888 555
样例输出
222
444
333
444
999
提示
1.1.1.nnn和mmm的数据范围:n,m≤50000n,m≤50000n,m≤50000
2.2.2.序列中每个数的数据范围:[0,1e8][0,1e8][0,1e8]
3.3.3.虽然原题没有,但事实上555操作的kkk可能为负数
前言:
线段树通常用来解决区间上的问题,而平衡树可以支持查询排名、前驱、后继、第kkk小等问题。
如果要支持动态的区间上的上述问题,就必须用树套树这种神奇的数据结构。(可能还有别的做法吧,但本蒟蒻只会线段树套平衡树)
具体来说,用线段树对原序列进行划分,每个节点上开一颗平衡树,用来维护该段区间元素权值的信息。
举个栗子:
设原序列是444 222 333 111 999 444 555 111
那么将原序列构造一颗线段树,如下图所示
如果要在第222~777个元素中求777的后继,那么我们就可以在[2,2],[3,4],[5,6],[7,7][2,2],[3,4],[5,6],[7,7][2,2],[3,4],[5,6],[7,7]中分别求出777的后继为+∞,+∞,9,+∞+∞,+∞,9,+∞+∞,+∞,9,+∞,对这些值取minminmin即可。
那么求前驱、排名也是类似的
关于如何求第kkk小数,我们可以二分答案, 求最大的xxx,使得在[l,r][l,r][l,r]中比xxx小的数的个数为k−1k-1k−1
时空复杂度分析:
- 空间复杂度:
线段树的树高是O(logn)O(\log n)O(logn),而线段树上每一层区间的并集都是nnn,即每一层的空间复杂度是O(n)O(n)O(n)的,所以总复杂度为O(nlogn)O(n \log n)O(nlogn) - 时间复杂度
对于求前驱、后继和排名,原询问区间会在线段树上划分成O(logn)O(\log n)O(logn)段区间,在每段区间中查询信息时间复杂度是O(logn)O(\log n)O(logn)的,所以总时间复杂度为O(log2n)O(\log^2 n)O(log2n)
对于求第kkk小,因为需要二分答案,所以在配合对原序列和修改的值离散化的情况下可以做到O(log3n)O(\log^3 n)O(log3n)(当然本人很懒,而且此题对常数没有太大的要求,就没有离散化)
代码
#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;
}