当前位置: 首页 > 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;
}
http://www.lryc.cn/news/570526.html

相关文章:

  • Ghost8.0分区备份与恢复详细图解
  • 分享亿个HTML炫酷特效代码
  • Windows7 32位 旗舰版 [轻度优化 2.6G]
  • SpringBoot电脑商城项目--用户注册功能
  • Static修饰的变量定义在头文件(.h)中的影响
  • 500G 史上最全的JAVA全套视频教程网盘
  • semi-BATNet
  • Kotlin实现文件上传进度监听:RequestBody封装详解
  • web前端学习(三)——HTML5的字体、特殊符号、插入图片及头部元素的相关标签设置
  • 摩托罗拉v8对讲机驱动软件_摩托罗拉驱动下载安装教程
  • Meta-Analysis
  • 开源加密软件 TrueCrypt使用方法(图)
  • Rviz2中,在rviz和launch文件中都需要配置urdf文件,二者作用上的区别?
  • WordPress开启多站点功能以及插件MU Domain Mapping教程
  • CToolBar的使用总结(2)
  • html设置文本框为只读
  • Android系统文件夹结构说明以及Android专有名词介绍
  • 概率期望DP
  • 我的友情链接
  • C++ STL常用二分查找算法
  • 王峰:创业就是长征,能扛才能称王
  • BT读出来MAC地址值跟NV不一样
  • 基础知识-军品软件六性
  • 函数指针的理解
  • MeeGo系统和SailFish系统_我是亲民_新浪博客
  • 介质访问控制——随机访问控制
  • AndroidStudio3.0全新安装和基本配置
  • LoadRunner8.1+汉化+破解
  • 可爱的字符表情
  • Python文件与目录操作管理详解