洛谷P3834【模板】可持久化线段树 1(主席树)

题目背景

这是个非常经典的主席树入门题——静态区间第K小

数据已经过加强,请使用主席树。同时请注意常数优化

题目描述

如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值。

输入输出格式

输入格式:

第一行包含两个正整数N、M,分别表示序列的长度和查询的个数。

第二行包含N个正整数,表示这个序列各项的数字。

接下来M行每行包含三个整数\(l, r, k\) , 表示查询区间\([l, r]\)内的第k小值。

输出格式:

输出包含k行,每行1个正整数,依次表示每一次查询的结果

输入输出样例

输入样例#1:

5 5
25957 6405 15770 26287 26465
2 2 1
3 4 1
4 5 1
1 2 2
4 4 1

输出样例#1:

6405
15770
26287
25957
26287

说明

数据范围:

对于20%的数据满足:\(1 \leq N, M \leq 10\)

对于50%的数据满足:\(1 \leq N, M \leq 10^3\)

对于80%的数据满足:\(1 \leq N, M \leq 10^5\)

对于100%的数据满足:\(1 \leq N, M \leq 2\cdot 10^5\)

对于数列中的所有数\(a_i\)​ ,均满足\(-{10}^9 \leq a_i \leq {10}^9\)

样例数据说明:

N=5,数列长度为5,数列从第一项开始依次为\([25957, 6405, 15770, 26287, 26465 ]\)

第一次查询为\([2, 2]\)区间内的第一小值,即为6405

第二次查询为\([3, 4]\)区间内的第一小值,即为15770

第三次查询为\([4, 5]\) 区间内的第一小值,即为26287

第四次查询为\([1, 2]\)区间内的第二小值,即为25957

第五次查询为\([4, 4]\) 区间内的第一小值,即为26287

题解

可持久化线段树模板题。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
inline int max(int a, int b){return a > b ? a : b;}
inline int min(int a, int b){return a < b ? a : b;}
inline void swap(int &x, int &y){int  tmp = x;x = y;y = tmp;}
inline void read(int &x)
{
    x = 0;char ch = getchar(), c = ch;
    while(ch < '0' || ch > '9') c = ch, ch = getchar();
    while(ch <= '9' && ch >= '0') x = x * 10 + ch - '0', ch = getchar();
    if(c == '-') x = -x;
}

const int INF = 0x3f3f3f3f;
const int MAXN = 200000 + 10;

struct Node
{
	int ls, rs, size;
	void clear(){ls = rs = size = 0;}
}node[MAXN * 40];
int n, m, stack[MAXN], top, tot, num[MAXN], cnt, fa[MAXN], pos[MAXN];
int newnode(){if(top) return stack[top --];return ++ tot;}
void delnode(int x){stack[++ top] = x;node[x].clear();}
struct Tmp{int rank,num;}tmp[MAXN];
bool cmp(Tmp a, Tmp b){return a.num < b.num;}
inline void pushup(int x){node[x].size = node[node[x].ls].size + node[node[x].rs].size;}
void build(int &o, int l = 1, int r = cnt)
{
	if(!o) o = newnode();
	if(l == r)	return;
	int mid = (l + r) >> 1;
	build(node[o].ls, l, mid);
	build(node[o].rs, mid + 1, r);
}
void insert(int &o, int oo, int p, int l = 1, int r = cnt)
{
	if(!o) o = newnode();
	node[o].size = node[oo].size + 1; 
	if(l == r)	return;
	int mid = (l + r) >> 1;
	if(p <= mid) insert(node[o].ls, node[oo].ls, p, l, mid), node[o].rs = node[oo].rs;
	else insert(node[o].rs, node[oo].rs, p, mid + 1, r), node[o].ls = node[oo].ls;
}
//l-1->o,r->oo, 第k小 
int ask(int o, int oo, int k, int l = 1, int r = cnt)
{
	if(l == r) return l;
	int mid = (l + r) >> 1, a = node[node[oo].ls].size - node[node[o].ls].size;
	if(a >= k) return ask(node[o].ls, node[oo].ls, k, l, mid);
	else return ask(node[o].rs, node[oo].rs, k - a, mid + 1, r);
}
int main()
{
	read(n), read(m);
	for(int i = 1;i <= n;++ i) read(tmp[i].num), tmp[i].rank = i;
	std::sort(tmp + 1, tmp + 1 + n, cmp);
	tmp[0].num = -INF;
	for(int i = 1;i <= n;++ i)
	{
		if(tmp[i].num > tmp[i - 1].num) ++ cnt, pos[cnt] = tmp[i].num;
		num[tmp[i].rank] = cnt;
	}
	build(fa[0]);
	for(int i = 1;i <= n;++ i) insert(fa[i], fa[i - 1], num[i]);
	for(int i = 1;i <= m;++ i)
	{
		int l, r, k;read(l), read(r), read(k);
		printf("%d\n", pos[ask(fa[l - 1], fa[r], k)]);
	}
 	return 0;
}
posted @ 2018-04-16 11:17  嘒彼小星  阅读(264)  评论(0编辑  收藏  举报