zl程序教程

您现在的位置是:首页 >  其他

当前栏目

【ybt金牌导航4-6-1】【luogu P3834】【模板】区间第k小 / 可持久化线段树 2(主席树)

模板 导航 持久 线段 区间 Luogu ybt 金牌
2023-09-27 14:28:25 时间

【模板】区间第k小 / 可持久化线段树 2(主席树)

题目链接:ybt金牌导航4-6-1 / luogu P3834

题目大意

对于一个数组,求一个区间第 k 小的值。

思路

这里假设已经会做可持续化线段树1

那你想想怎么弄。
首先这道题数很大,但是真实个数又很小,而且只是比较大小关系。那我们可以把它离散化。
那离散化有什么用呢?可以放进数组里,显示这个数有多少个。

那我们可以用线段树存,存某个数到某个数之间出现了多少次。
但是两边都不确定不好弄,我们就确定左边是最左边。

那你就一开始树上都是 0 0 0,因为你一个数都没有。
然后你每次插一个数,就像弄可持续化线段树一样弄。

然后你考虑怎么查询。
你可以用前缀和的方法,两个树各个位置相减,就得到了区间对应的树。当然这个树不用构造出来,你只要走的时候减一下就可以。

然后你线段树记录它子树的值的和,就可以找到第 k 小了。

代码

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

struct node {
	int x, num, xx;
}a[200001];
struct Tree {
	int l, r, x;
}tree[200001 << 5];
int n, m, num, dy[200001];
int root[200001], tot, L, R, K;

bool cmp(node x, node y) {
	return x.x < y.x;
}

bool cmp_b(node x, node y) {
	return x.num < y.num;
}

int clone(int now) {//可持续化线段树
	tot++;
	tree[tot] = tree[now];
	return tot;
}

int build(int now, int l, int r) {
	now = ++tot;
	
	if (l == r) {
		return now;
	}
	
	int mid = (l + r) >> 1;
	tree[now].l = build(tree[now].l, l, mid);
	tree[now].r = build(tree[now].r, mid + 1, r);
	
	return now;
}

int add(int now, int l, int r, int loc, int add_num) {
	now = clone(now);
	
	tree[now].x += add_num;
	
	if (l == r) return now;
	
	int mid = (l + r) >> 1;
	if (loc <= mid) tree[now].l = add(tree[now].l, l, mid, loc, add_num);
		else tree[now].r = add(tree[now].r, mid + 1, r, loc, add_num);
	
	return now;
}

int ask(int now1, int now2, int l, int r, int loc) {
	if (l == r) return l;
	
	int mid = (l + r) >> 1;
	if (loc <= tree[tree[now1].l].x - tree[tree[now2].l].x) return ask(tree[now1].l, tree[now2].l, l, mid, loc);
		else return ask(tree[now1].r, tree[now2].r, mid + 1, r, loc - (tree[tree[now1].l].x - tree[tree[now2].l].x));
}

int main() {
	scanf("%d %d", &n, &m);
	
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i].x);
		a[i].num = i;
	}
	sort(a + 1, a + n + 1, cmp);
	
	a[1].xx = ++num;//离散化
	dy[1] = a[1].x;
	for (int i = 2; i <= n; i++)
		if (a[i].x != a[i - 1].x) {
			a[i].xx = ++num;
			dy[num] = a[i].x;
		}
		else a[i].xx = num;
	
	sort(a + 1, a + n + 1, cmp_b);
	
	root[0] = build(0, 1, num);
	
	for (int i = 1; i <= n; i++)
		root[i] = add(root[i - 1], 1, num, a[i].xx, 1);
	
	for (int i = 1; i <= m; i++) {
		scanf("%d %d %d", &L, &R, &K);
		
		//类似前缀和的思想
		printf("%d\n", dy[ask(root[R], root[L - 1], 1, num, K)]);
	}
	
	return 0;
}