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_ralar 中第 kkk 小的数.
  • C x y:令 ax←ya_x\gets yaxy.

Limitations

1≤n,q≤1051\le n,q\le 10^51n,q105
1≤l≤r≤n,1≤k≤r−l+11\le l\le r\le n,1\le k\le r-l+11lrn,1krl+1
1≤x≤n,1≤ai,y≤1091\le x\le n,1\le a_i,y\le 10^91xn,1ai,y109
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 mm 的数个数,考虑 QQQ 中的每个操作:

  • 若为查询操作,我们在 BIT 上查 al∼ara_l\sim a_ralar≤m\le mm 的数个数(设为 ppp),如果 p≥kp\ge kpk,就把 TTT 放到 QLQ_LQL 中,否则令 k←k−pk\gets k-pkkp 后放到 QRQ_RQR 中.

  • 若为插入或删除操作,如果 y≤my\le mym,那么就在 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(log⁡n)O(\log n)O(logn) 层,BIT 单次修改 O(log⁡n)O(\log n)O(logn),故时间复杂度为 O(nlog⁡2n)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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值