Description
给定序列 a = ( a 1 , a 2 , ⋯ , a n ) a=(a_1,a_2,\cdots,a_n) a=(a1,a2,⋯,an),有 m m m 次操作分三种:
- kth ( l , r , k ) \operatorname{kth}(l,r,k) kth(l,r,k):求 a l ∼ a r a_l\sim a_r al∼ar 中第 k k k 小的值.
- modify ( i , x ) \operatorname{modify}(i,x) modify(i,x):将 a i a_i ai 改为 x x x.
- insert ( i , x ) \operatorname{insert}(i,x) insert(i,x):在 a i a_i ai 前插入 x x x.
Limitations
1
≤
n
≤
35000
1\le n\le 35000
1≤n≤35000
0
≤
a
i
,
x
≤
70000
\textcolor{red}{0}\le a_i,x\le 70000
0≤ai,x≤70000
insert
\operatorname{insert}
insert 操作少于
35000
35000
35000 次.
另两种操作分别少于
70000
70000
70000 次.
2
s
,
500
MB
2\text{s},500\text{MB}
2s,500MB
Solution
注意到
n
n
n 和
V
V
V 都很小,可以考虑用 P4119 的做法.
对序列
a
a
a 分块,再对值域
[
0
,
70000
]
[0,70000]
[0,70000] 分块,设块长为
B
B
B(这里取
265
265
265),在每块内维护:
- pre i , j \textit{pre}_{i,j} prei,j:第 1 ∼ i 1\sim i 1∼i 个序列块中数值 j j j 的出现次数.
- bpre i , j \textit{bpre}_{i,j} bprei,j:第 1 ∼ i 1\sim i 1∼i 个序列块中第 j j j 个值域块内数的出现次数.
接着考虑每个操作:
-
kth
\operatorname{kth}
kth:同
P4119
处理. - modify \operatorname{modify} modify:直接在块内修改,并更新 pre \textit{pre} pre 和 bpre \textit{bpre} bpre.
- insert \operatorname{insert} insert:直接在块内暴力插入,同样要更新两个数组,以及后面的块要全部后移一位.
然而不断插入后,块的大小会很长,从而影响效率,所以当块大小超过某个阈值(比如 2 × B 2\times B 2×B)后,需要把这个块裂成两个.
实现上,每个块的信息用结构体保存,其中块内序列用 vector
维护,插入只需调用 vector::insert
.
外层用 list
存所有块,分裂时可以
O
(
1
)
O(1)
O(1) 插入一个块,查找块时就暴力扫一遍,不影响复杂度,写的时候注意细节(见代码).
Code
3.69 KB , 2.32 s , 72.59 MB (in total, C++20 with O2) 3.69\text{KB},2.32\text{s},72.59\text{MB}\;\texttt{(in total, C++20 with O2)} 3.69KB,2.32s,72.59MB(in total, C++20 with O2)
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;
template<class T>
bool chmax(T &a, const T &b){
if(a < b){ a = b; return true; }
return false;
}
template<class T>
bool chmin(T &a, const T &b){
if(a > b){ a = b; return true; }
return false;
}
constexpr int V = 7e4 + 10, B = 265, M = (V + B - 1) / B;
inline int bel(int x) { return x / B; }
struct Block {
int L = -1, R = -1, len = 0;
vector<int> seq, pre, bpre;
inline Block() {}
inline void init(int l, int r, const vector<int>::iterator a, const Block& last = {}) {
L = l, R = r, len = r - l + 1;
seq.resize(len);
pre.resize(V);
bpre.resize(M);
for (int i = 0; i < len; i++) add(seq[i] = a[i], 1);
if (last.L == -1) return;
for (int i = 0; i < V; i++) pre[i] += last.pre[i];
for (int i = 0; i < M; i++) bpre[i] += last.bpre[i];
}
inline void add(int x, int v) {
pre[x] += v, bpre[bel(x)] += v;
}
inline void move() { L++, R++; }
inline void insert(int x, int v) {
seq.insert(seq.begin() + x, v);
R++, len++;
}
inline void set(int x, int v) { seq[x] = v; }
inline void push(int l, int r, int t, vector<int>& tmp) {
for (int i = l; i <= r; i++) {
if (t == 0) tmp[bel(seq[i])]++;
else tmp[seq[i]]++;
}
}
};
using Iter = list<Block>::iterator;
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
const int blocks = (n + B - 1) / B;
list<Block> blks;
auto init = [&]() {
for (int i = 0; i < blocks; i++) {
Block cur;
const int bl = i * B, br = min(bl + B, n) - 1;
if (i == 0) cur.init(bl, br, a.begin() + bl);
else cur.init(bl, br, a.begin() + bl, blks.back());
blks.push_back(cur);
}
};
auto find = [&](int x) {
for (Iter it = blks.begin(); it != blks.end(); it++)
if (it->L <= x && x <= it->R) return it;
return prev(blks.end());
};
auto refact = [&](Iter b) {
Iter pb = prev(b);
Block ta, tb;
const int mid = (b->L + b->R) >> 1;
if (b == blks.begin()) ta.init(b->L, mid, b->seq.begin());
else ta.init(b->L, mid, b->seq.begin(), *pb);
tb.init(mid + 1, b->R, b->seq.begin() + (mid - b->L) + 1, ta);
blks.insert(b, ta);
blks.insert(b, tb);
blks.erase(b);
};
vector<int> tmp(V);
auto push = [&](int l, int r, Iter bl, Iter br, int t) {
if (bl == br) bl->push(l - bl->L, r - bl->L, t, tmp);
else {
bl->push(l - bl->L, bl->len - 1, t, tmp);
br->push(0, r - br->L, t, tmp);
}
};
auto kth = [&](int l, int r, int k) {
Iter bl = find(l), br = find(r), pbr = prev(br);
for (int i = 0; i < M; i++) tmp[i] = (bl != br) ? (pbr->bpre[i] - bl->bpre[i]) : 0;
push(l, r, bl, br, 0);
int sum = 0, blk = -1;
for (int i = 0; i < M; i++) {
sum += tmp[i];
if (sum >= k) {
blk = i;
sum -= tmp[i];
break;
}
}
if (blk == -1) return -1;
const int fl = blk * B, fr = min(fl + B, V) - 1;
for (int i = fl; i <= fr; i++) tmp[i] = (bl != br) ? (pbr->pre[i] - bl->pre[i]) : 0;
push(l, r, bl, br, 1);
for (int i = fl; i <= fr; i++) {
sum += tmp[i];
if (sum >= k) return i;
}
return -1;
};
auto update = [&](int x, int v) {
Iter b = find(x);
const int u = b->seq[x - b->L];
for (Iter it = b; it != blks.end(); it++) it->add(u, -1), it->add(v, 1);
b->set(x - b->L, v);
};
auto insert = [&](int x, int v) {
Iter b = find(x);
b->insert(x - b->L, v);
for (Iter it = b; it != blks.end(); it++) {
if (it != b) it->move();
it->add(v, 1);
}
if (b->len >= (B << 1)) refact(b);
};
init();
int m; cin >> m;
for (int i = 0, x, y, k, lst = 0; i < m; i++) {
char op; cin >> op >> x >> y;
x ^= lst, y ^= lst;
if (op == 'Q') {
cin >> k;
x--, y--, k ^= lst;
cout << (lst = kth(x, y, k)) << '\n';
}
if (op == 'M') x--, update(x, y);
if (op == 'I') x--, insert(x, y);
}
return 0;
}