LG P2617 Dynamic Rankings Solution
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;
}