LG P4278 带插入区间K小值 Solution
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;
}