主席树模板 区间第k大数

题目入口

首先看到n的个数很小,但是数据范围很大的情况下,我们一般优先会选择离散化然后再建树 。
故第一步我们要先对其进行一个离散化 。

第二步是关键思想 - 可持久化数据结构,这是一个保存历史数据然后解决问题的方法, 在这里,我们保留每添加一个数的数据作为历史线段树 , 然后对其进行一个类似于前缀和的路子进行查找答案。

思路很简单,但是具体的细节仍需代码实现去完善逻辑思维。

#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cstdio>

using namespace std ;

const int N = 2e5 + 10 ;

int read()
{
	int res = 0 , flag = 1 ;
	char c = getchar() ;
	while(!isdigit(c))
	{
		if(c == '-') flag = -1 ;
		c = getchar() ;
	}
	while(isdigit(c))
	{
		res = (res << 1) + (res << 3) + (c ^ 48) ;
		c = getchar() ;
	}
	return res * flag ;
}

struct Node
{
	int l , r ;
	int val ;	
} tr[N * 40] ;
int cnt , root[N] ;
vector<int> res ;
int n , m , Datas[N] ;

int get_id(int x)
{
	return lower_bound(res.begin() , res.end() , x) - res.begin();
}

void build(int &x , int l , int r)
{
	x = ++ cnt ;
    tr[x].val = 0 ;
	if(l == r) return ;
	int mid = l + r >> 1 ;
	build(tr[x].l , l , mid) ;
	build(tr[x].r , mid + 1 , r) ;
}

void insert(int l , int r , int pre , int &now , int pos)
{
	now = ++ cnt ;
	tr[now] = tr[pre] ;
	tr[now].val ++ ;
	if(l == r) return ;
	int mid = l + r >> 1 ;
	if(pos <= mid) insert(l , mid , tr[pre].l , tr[now].l , pos) ;
	else insert(mid + 1 , r , tr[pre].r , tr[now].r , pos) ;
}

int query(int l , int r , int x , int y , int k)
{
	if(l == r) return l ;
	int tmp = tr[tr[y].l].val - tr[tr[x].l].val ;
	int mid = l + r >> 1 ;
	if(tmp >= k) return query(l , mid , tr[x].l , tr[y].l , k) ;
	else return query(mid + 1 , r , tr[x].r , tr[y].r , k - tmp) ; 
}
int main(void)
{
	n = read() , m = read() ;
	build(root[0] , 1 , n) ;
	res.push_back(-1e9 + 2) ;
	for(int i = 1 ; i <= n ; i ++)
	{
		int t = read() ;
		res.push_back(t) ;
		Datas[i] = t ;
	}
	sort(res.begin() , res.end()) ;
	res.erase(unique(res.begin() , res.end()) , res.end()) ;
	
	for(int i = 1 ; i <= n ; i ++)
		insert(1 , n , root[i - 1] , root[i] , get_id(Datas[i])) ;
	for(int i = 1 ; i <= m ; i ++)
	{
		int l , r , k ;
		l = read() , r = read() , k = read() ;
		printf("%lld\n" , res[query(1 , n , root[l - 1] , root[r] , k)]) ;
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值