题解 P2617 Dynamic Rankings

题目描述

Link

给定一个含有 \(n\) 个数的序列 \(a_1,a_2 \dots a_n\) ,需要进行 \(m\) 次操作,每次操作是下面的两种之一:

  • Q l r k 表示查询下标在区间 \([l,r]\) 中的第 \(k\) 小的数
  • C x y 表示将 \(a_x\) 改为 \(y\)

\(1 \leq n ,m \leq 10^5 ,1 \leq a_i,y \leq 10^9\)

Solution

P3332 [ZJOI2013]K大数查询 基本上一样,大部分内容在前面的题解 题解 P3332 [ZJOI2013]K大数查询 已经写过了。

由于只有单点修改,所以本题直接用权值线段树套动态开点单点修改线段树就可以。

对于本题的 C 操作,我们可以看成在 \(x\) 这个位置删掉了一个 \(a_x\) ,又加上了一个 \(y\) 之后的结果。

所以我们可以分成两次操作:

  • 在权值线段树内找到权值为 \(a_x\) 的点,然后把一路上的所有点所对应的内层线段树的 \(x\) 这个位置上 \(-1\)
  • 在权值线段树内找到权值为 \(y\) 的点,然后把一路上的所有点所对应的内层线段树的 \(x\) 这个位置上 \(+1\)

同样的,本题需要离散化,所以要先读入所有的操作再进行操作。

两个坑点:

  • 和上题不同的是,这题是找 \(k\) 小,而不是 \(k\) 大,所以我们在查询的时候要先看左儿子落在 \([l,r]\) 的数量,如果不足 \(k\) 个就进入右儿子。
  • 离散化的时候原数组记得也要离散化。

代码如下:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
inline int read() {
	int num = 0 ,f = 1; char c = getchar();
	while (!isdigit(c)) f = c == '-' ? -1 : f ,c = getchar();
	while (isdigit(c)) num = (num << 1) + (num << 3) + (c ^ 48) ,c = getchar();
	return num * f;
}
inline int max(int a ,int b) {return a > b ? a : b;}
inline int min(int a ,int b) {return a < b ? a : b;}
const int N = 1e5 + 5 ,M = N << 1 ,S = N * 17 * 17;
struct Segment1 {
	struct node {
		int l ,r ,sum;
		node (int l = 0 ,int r = 0 ,int sum = 0) : l(l) ,r(r) ,sum(sum) {}
	}t[S]; int tot;
	Segment1 () : tot(0) {}
	inline void update(int now) {
		t[now].sum = t[t[now].l].sum + t[t[now].r].sum;
	}
	inline void modify(int &now ,int l ,int r ,int q ,int k) {
		if (!now) now = ++tot;
		if (l == r) {t[now].sum += k; return ;}
		int mid = (l + r) >> 1;
		if (q <= mid) modify(t[now].l ,l ,mid ,q ,k);
		else modify(t[now].r ,mid + 1 ,r ,q ,k);
		update(now);
	}
	inline int query(int &now ,int l ,int r ,int ql ,int qr) {
		if (!now) return 0;
		if (ql <= l && r <= qr) return t[now].sum;
		int mid = (l + r) >> 1 ,ans = 0;
		if (ql <= mid) ans += query(t[now].l ,l ,mid ,ql ,qr);
		if (qr > mid) ans += query(t[now].r ,mid + 1 ,r ,ql ,qr);
		return ans;
	}
}u;
int n;
struct Segment {
	int t[M << 2];
	Segment () {memset(t ,0 ,sizeof(t));}
	inline void modify(int now ,int l ,int r ,int p ,int q ,int k) {
		u.modify(t[now] ,1 ,n ,p ,k);
		if (l == r) return ;
		int mid = (l + r) >> 1;
		if (q <= mid) modify(now << 1 ,l ,mid ,p ,q ,k);
		else modify(now << 1 | 1 ,mid + 1 ,r ,p ,q ,k);
	}
	inline int query(int now ,int l ,int r ,int ql ,int qr ,int k) {
		if (l == r) return l;
		int mid = (l + r) >> 1 ,res = u.query(t[now << 1] ,1 ,n ,ql ,qr);
		if (res >= k) return query(now << 1 ,l ,mid ,ql ,qr ,k); //先判断左儿子
		return query(now << 1 | 1 ,mid + 1 ,r ,ql ,qr ,k - res);
	}
}t;
int m ,nums[M] ,tot;
inline int find(int val) {
	return lower_bound(nums + 1 ,nums + tot + 1 ,val) - nums;
}
struct Query {
	int opt ,x ,y ,k;
	Query (int opt = 0 ,int x = 0 ,int y = 0 ,int k = 0) :
		opt(opt) ,x(x) ,y(y) ,k(k) {}
}q[N];
char opt[3];
int a[N];
signed main() {
	n = read(); m = read();
	for (int i = 1; i <= n; i++)
		a[i] = read() ,nums[++tot] = a[i];
	for (int i = 1; i <= m; i++) {
		scanf("%s" ,opt); int x = read() ,y = read();
		if (opt[0] == 'Q') {
			int k = read();
			q[i] = Query(2 ,x ,y ,k);
		}
		else {
			nums[++tot] = y;
			q[i] = Query(1 ,x ,y);
		}
	}
	sort(nums + 1 ,nums + tot + 1);
	tot = unique(nums + 1 ,nums + tot + 1) - nums - 1;
	for (int i = 1; i <= n; i++) { //记得把 a[i] 离散化
		a[i] = find(a[i]);
		t.modify(1 ,1 ,tot ,i ,a[i] ,1);
	}
	for (int i = 1; i <= m; i++) {
		if (q[i].opt == 2)
			printf("%d\n" ,nums[t.query(1 ,1 ,tot ,q[i].x ,q[i].y ,q[i].k)]);
		else {
			int c = find(q[i].y);
			t.modify(1 ,1 ,tot ,q[i].x ,a[q[i].x] ,-1);
			t.modify(1 ,1 ,tot ,q[i].x ,c ,1);
			a[q[i].x] = c;
		}
	}
	return 0;
}
posted @ 2021-03-07 14:24  recollector  阅读(56)  评论(0编辑  收藏  举报