Preface
刚才在另一篇题解里提到了这个题,来补一下.
Description
给定序列 a=(a1,a2,⋯ ,an)a=(a_1,a_2,\cdots,a_n)a=(a1,a2,⋯,an),有 qqq 次操作分两种:
Q l r k:求 al∼ara_l\sim a_ral∼ar 中第 kkk 小的数.C x y:令 ax←ya_x\gets yax←y.
Limitations
1≤n,q≤1051\le n,q\le 10^51≤n,q≤105
1≤l≤r≤n,1≤k≤r−l+11\le l\le r\le n,1\le k\le r-l+11≤l≤r≤n,1≤k≤r−l+1
1≤x≤n,1≤ai,y≤1091\le x\le n,1\le a_i,y\le 10^91≤x≤n,1≤ai,y≤109
3s,512MB3\text{s},512\text{MB}3s,512MB
Solution
题目没有强制在线,可以全部离线下来.
由于要求第 kkk 小,自然想到整体二分.
首先对所有 ai,ya_i,yai,y 离散化,并把 C 操作拆成一次删除 aia_iai 和一次插入 yyy.
设当前处理到值域区间(离散化后)为 [s,t][s,t][s,t],mmm 为当前的中点,QQQ 为当前的操作列表.
若 s=ts=ts=t 则每个查询的答案都为 sss.
否则,我们用一个 BIT 来维护当前 aaa 中 ≤m\le m≤m 的数个数,考虑 QQQ 中的每个操作:
-
若为查询操作,我们在
BIT上查 al∼ara_l\sim a_ral∼ar 中 ≤m\le m≤m 的数个数(设为 ppp),如果 p≥kp\ge kp≥k,就把 TTT 放到 QLQ_LQL 中,否则令 k←k−pk\gets k-pk←k−p 后放到 QRQ_RQR 中. -
若为插入或删除操作,如果 y≤my\le my≤m,那么就在
BIT上修改,并放入 QLQ_LQL,否则直接放入 QRQ_RQR.
处理全部操作后,递归 ([s,m],QL)([s,m],Q_L)([s,m],QL) 和 ([m+1,t],QR)([m+1,t],Q_R)([m+1,t],QR) 继续处理,不过在这之前需要撤掉 BIT 上的所有修改.
至于理解,可以想一下 qqq 次二分同步进行.
由于要分治 O(logn)O(\log n)O(logn) 层,BIT 单次修改 O(logn)O(\log n)O(logn),故时间复杂度为 O(nlog2n)O(n\log^2 n)O(nlog2n),空间复杂度 O(n)O(n)O(n),但比树套树常数小得多.
Code
3.95KB,2.86s,17.44MB (c++20 with o2)3.95\text{KB},2.86\text{s},17.44\text{MB}\;\texttt{(c++20 with o2)}3.95KB,2.86s,17.44MB(c++20 with o2)
// Problem: P2617 Dynamic Rankings
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2617
// Memory Limit: 512 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#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;
}
struct Node {
int tp, val;
int le, ri;
int pos;
Node(int _tp = 0, int _val = 0, int _le = 0, int _ri = 0, int _pos = 0):
tp(_tp), val(_val), le(_le), ri(_ri), pos(_pos) {}
};
int lowbit(int x){
return x & -x;
}
template<class T>
struct fenwick{
int n;
vector<T> c;
fenwick() {}
fenwick(int _n): n(_n){
c.resize(n + 1);
}
fenwick(const vector<T> &a): n(a.size()){
c.resize(n + 1);
for(int i = 1; i <= n; i++){
c[i] = c[i] + a[i - 1];
int j = i + lowbit(i);
if(j <= n) c[j] = c[j] + c[i];
}
}
void add(int x, const T& v){
for(int i = x + 1; i <= n; i += lowbit(i)) c[i] = c[i] + v;
}
T ask(int x){
T ans{};
for(int i = x + 1; i; i -= lowbit(i)) ans = ans + c[i];
return ans;
}
T ask(int l, int r){
return ask(r) - ask(l - 1);
}
};
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
vector<int> a(n), nums;
vector<Node> tasks, tmp;
for (int i = 0; i < n; i++) {
cin >> a[i];
nums.push_back(a[i]);
tasks.emplace_back(1, a[i], -1, -1, i);
}
for (int i = 0; i < m; i++) {
char op;
cin >> op;
if (op == 'Q') {
int l, r, k;
cin >> l >> r >> k;
l--, r--;
tasks.emplace_back(0, k, l, r, i);
}
else {
int p, v;
cin >> p >> v;
p--;
tasks.emplace_back(-1, a[p], -1, -1, p);
tasks.emplace_back(1, v, -1, -1, p);
nums.push_back(v);
a[p] = v;
}
}
sort(nums.begin(), nums.end());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
for (auto &task : tasks) {
if (task.tp == 0) {
continue;
}
int p = lower_bound(nums.begin(), nums.end(), task.val) - nums.begin();
task.val = p;
}
vector<int> ans(m, -1);
fenwick<int> f(n);
auto solve = [&](auto &&self, int l, int r, int lo, int hi) -> void {
if (l >= r) {
return;
}
if (lo == hi) {
for (int i = l; i <= r; i++) {
if (tasks[i].tp == 0) {
ans[tasks[i].pos] = lo;
}
}
return;
}
int mid = (lo + hi) / 2;
vector<Node> tl, tr;
for (int i = l; i <= r; i++) {
if (tasks[i].tp) {
if (tasks[i].val <= mid) {
f.add(tasks[i].pos, tasks[i].tp);
tl.push_back(tasks[i]);
}
else {
tr.push_back(tasks[i]);
}
}
else {
int cnt = f.ask(tasks[i].le, tasks[i].ri);
if (cnt >= tasks[i].val) {
tl.push_back(tasks[i]);
}
else {
tasks[i].val -= cnt;
tr.push_back(tasks[i]);
}
}
}
for (int i = l; i <= r; i++) {
if (tasks[i].tp && tasks[i].val <= mid) {
f.add(tasks[i].pos, -tasks[i].tp);
}
}
copy(tl.begin(), tl.end(), tasks.begin() + l);
copy(tr.begin(), tr.end(), tasks.begin() + l + tl.size());
self(self, l, l + tl.size() - 1, lo, mid);
self(self, l + tl.size(), r, mid + 1, hi);
};
solve(solve, 0, tasks.size() - 1, 0, nums.size() - 1);
for (int i = 0; i < m; i++) {
if (ans[i] != -1) {
cout << nums[ans[i]] << endl;
}
}
return 0;
}
653

被折叠的 条评论
为什么被折叠?



