116 lines
3.6 KiB
C++
116 lines
3.6 KiB
C++
#include <iostream>
|
||
#include <cstring>
|
||
#include <algorithm>
|
||
|
||
using namespace std;
|
||
//快读
|
||
int read() {
|
||
int x = 0, f = 1;
|
||
char ch = getchar();
|
||
while (ch < '0' || ch > '9') {
|
||
if (ch == '-') f = -1;
|
||
ch = getchar();
|
||
}
|
||
while (ch >= '0' && ch <= '9') {
|
||
x = (x << 3) + (x << 1) + (ch ^ 48);
|
||
ch = getchar();
|
||
}
|
||
return x * f;
|
||
}
|
||
|
||
const int N = 300010;
|
||
int n, m;
|
||
int a[N]; //维护的序列
|
||
int ans[N]; // 结果
|
||
|
||
struct Node {
|
||
// op=1 插入, op=-1 删除, op=2 查询
|
||
int op;
|
||
// 插入:l:插入的值,用于二分与mid PK,r:位置
|
||
// 删除:l:删除的值,用于二分与mid PK,r:位置
|
||
// 查询 [l,r]:查询范围
|
||
// k:第k小
|
||
int l, r, k;
|
||
int id;
|
||
} q[N], lq[N], rq[N];
|
||
|
||
//树状数组
|
||
int tr[N];
|
||
void add(int x, int c) {
|
||
for (; x < n; x += x & -x) tr[x] += c;
|
||
}
|
||
//查询x的个数和
|
||
int getSum(int x) {
|
||
int sum = 0;
|
||
for (; x; x -= x & -x) sum += tr[x];
|
||
return sum;
|
||
}
|
||
|
||
void solve(int l, int r, int ql, int qr) {
|
||
if (ql > qr) return; //防止RE,递归边界
|
||
|
||
if (l == r) { //递归到叶子结点,找到答案
|
||
for (int i = ql; i <= qr; i++)
|
||
if (q[i].op == 2) ans[q[i].id] = l; //所有查询请求,标识答案
|
||
return;
|
||
}
|
||
|
||
int mid = (l + r) >> 1;
|
||
int nl = 0, nr = 0;
|
||
|
||
for (int i = ql; i <= qr; i++) {
|
||
if (q[i].op < 2) { // 非查询,插入:1 或 删除:-1
|
||
if (q[i].l <= mid) //按插入、删除的值与mid相对比进行左右决策
|
||
add(q[i].r, q[i].op), lq[++nl] = q[i]; //位置是q[i].r,如果是插入,则+1,如果是删除则-1
|
||
else
|
||
rq[++nr] = q[i];
|
||
} else {
|
||
//查询数组二分
|
||
int t = getSum(q[i].r) - getSum(q[i].l - 1);
|
||
if (q[i].k <= t)
|
||
lq[++nl] = q[i];
|
||
else {
|
||
q[i].k -= t;
|
||
rq[++nr] = q[i];
|
||
}
|
||
}
|
||
}
|
||
|
||
// 有借有还,再借不难
|
||
for (int i = ql; i <= qr; i++)
|
||
if (q[i].op < 2)
|
||
if (q[i].l <= mid) add(q[i].r, -q[i].op);
|
||
|
||
for (int i = 1; i <= nl; i++) q[ql - 1 + i] = lq[i]; //将左儿子操作数组,拷贝到q中,原来的下标是[0~ql-1],现在无缝接上[ql-1+1~ql-1+nl]
|
||
for (int i = 1; i <= nr; i++) q[ql - 1 + nl + i] = rq[i]; //将右儿子操作数组,拷贝到q中,原来的下标是[ql-1+nl+i]
|
||
|
||
//继续二分左右儿子
|
||
solve(l, mid, ql, ql + nl - 1), solve(mid + 1, r, ql + nl, qr);
|
||
}
|
||
|
||
int main() {
|
||
int l, r, k, c = 0, qc = 0; // c:总的操作次数,qc:查询次数
|
||
n = read(), m = read();
|
||
for (int i = 1; i <= n; i++) a[i] = read(), q[++c] = {1, a[i], i}; // op=1:插入,a[i]:值(用于与mid进行决策左右前进),i:位置
|
||
|
||
char op[2];
|
||
for (int i = 1; i <= m; i++) {
|
||
scanf("%s", op);
|
||
if (op[0] == 'Q') {
|
||
l = read(), r = read(), k = read();
|
||
q[++c] = {2, l, r, k, ++qc}; // op=2:查询,[l,r]:区间,k:第k小,++qc:查询的个数
|
||
} else {
|
||
int x = read(), y = read(); //修改操作
|
||
q[++c] = {-1, a[x], x}; // op=-1:删除,数值:a[x],位置:x
|
||
q[++c] = {1, y, x}; // op=1:插入,数值:y,位置:x
|
||
a[x] = y; //因为a[x]上面有用,所以只能用y临时读入一下,再更新a[x],因为可能存在重复修改,a[x]必须更新
|
||
}
|
||
}
|
||
//整体二分
|
||
solve(-1e9, 1e9, 1, c);
|
||
|
||
//结果
|
||
for (int i = 1; i <= qc; i++) printf("%d\n", ans[i]);
|
||
return 0;
|
||
}
|