You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

116 lines
3.6 KiB

2 years ago
#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;
}