当前位置: 首页 > news >正文

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 alar 中第 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 1n35000
0 ≤ a i , x ≤ 70000 \textcolor{red}{0}\le a_i,x\le 70000 0ai,x70000
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 1i序列块数值 j j j 的出现次数.
  • bpre i , j \textit{bpre}_{i,j} bprei,j:第 1 ∼ i 1\sim i 1i序列块中第 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;
}

相关文章:

  • Java的接口
  • 阿帕奇基金会软件授权与公司贡献者许可协议(中英双语版)
  • 基于CATIA轴系的最小边界曲面自动化生成技术深度解析
  • 力扣的SQL
  • 搭建基于 Prometheus、Grafana 和 Alertmanager 的监控告警系统
  • 「Linux文件及目录管理」文件内容的显示和处理类命令
  • 揭开MongoDB的神秘面纱:从陌生到初识
  • 如何修改PyCharm的界面颜色(超详细)
  • 手写简版React-router
  • 2025ICRA 最佳论文解读:麻省理工PolyTouch:一种多模态触觉传感器以及基于触觉扩散策略的接触丰富操作方法
  • 【学习笔记】深入理解Java虚拟机学习笔记——第9章 类加载及执行子系统的案例与实战
  • C语言进阶:深度解剖数据在内存中的存储(浮点型在内存中的存储)
  • Linux系统firewall-offline-cmd命令在企业网络安全防护中的应用案例分析
  • 2.RMII的时钟模式
  • Python训练营打卡 Day54
  • 【LangChain】5 评估
  • WebAssembly 2.0:超越浏览器的全栈计算革命
  • 修改Rocky Linux文件夹颜色
  • LLM对话框项目 EventSource封装和MessageServiceClass流式展示封装
  • Spring Boot的Security安全控制——应用SpringSecurity!
  • 唐山做网站的电话/搜索引擎营销的特点是什么
  • 企业网站个人备案吗/seo是什么专业
  • 施工企业承揽业务不良行为/优化大师怎么卸载
  • 开发公司销售房产交哪些税/网站服务器速度对seo有什么影响
  • 聊城网站建设价位/网络营销推广流程
  • 建设交通人才网站/山东百度推广