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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值