序列内第k小查询(线段树)

最近请教了一下大佬怎么求序列内第k大查询,自己又捣鼓了一下,虽然还没有懂得区间第k大查询,不过姑且做一个记录先吧

因为每个元素大小可能很大而元素之间不连续,所以我们先离散化处理一下,程序中的ori[ ]代表原序列,离散化后每个key对应一个值,mem[ ]存的是key对应的值:mem[i]代表离散化后 i 代表的值,a[i]代表离散化后有几个i对应的值(mem[i]的个数)

离散化之后建树,sum中存的是有序的元素总个数具体可以看程序,那么如何查询?我们查询线段树的sum,若tree[lid].sum >= k ,说明第k大一定在左儿子区间,因为线段树是以有序数列来构建的,每个叶子排起来是有序的(抠一下手指就出来了),说明k在左边。同理 若 tree[lid].sum < k 说明左边全部在一起都没有 k 个,答案自然就在右边了

因为从右边区间出发,已经排除全部左边节点了,所以k要减去左边的sum

这样一直找到叶子节点,找到的叶子对应的就是离散化后的key了,我们在利用mem输出就好

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
int RD(){
    int out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
#define lid (id << 1)
#define rid (id << 1) | 1
const int maxn = 100019;
int num,na;
int ori[maxn];
int mem[maxn];
struct sag_tree{
	int l,r,sum;
	}tree[maxn << 2];
int a[maxn];
void build(int id,int l,int r){
	tree[id].l = l;
	tree[id].r = r;
	if(l == r){
		tree[id].sum = a[l];
		return ;
		}
	int mid = (l + r) >> 1;
	build(lid,l,mid);
	build(rid,mid + 1,r);
	tree[id].sum = tree[lid].sum + tree[rid].sum;
	}
	
int query(int id,int k){
	if(tree[id].l == tree[id].r)return tree[id].l;
	if(tree[lid].sum < k)return query(rid,k - tree[lid].sum);
	else if(tree[lid].sum >= k)return query(lid,k);
	}
	
int main(){
	num = RD();na = RD();
	for(int i = 1;i <= num;i++){
		ori[i] = RD();
		}
	sort(ori + 1,ori + 1 + num);
	int n = 0;
	for(int i = 1;i <= num;i++){
		if(i == 1 || ori[i] != ori[i - 1]){
			n++;
			}
		a[n]++;
		mem[n] = ori[i];
		}
	build(1,1,n);
	int k;
	for(int i = 1;i <= na;i++){
		k = RD();
		cout<<mem[query(1,k)]<<endl;
		}
	return 0;
	}

推一下可以得到,序列第k大就是序列第(num - k + 1)小,所以很好解决


upd 18.7.9

后面发现,其实这也是treap查找序列第k大的方法

posted @ 2018-07-09 12:31  Tony_Double_Sky  阅读(226)  评论(0编辑  收藏  举报