K-th Number Poj

K-th Number Poj - 2104 主席树

题意

给你n数字,然后有m次询问,询问一段区间内的第k小的数。

解题思路

这个题是限时训练做的题,我不会,看到这个题我开始是拒绝的,虽然题意清晰简单,但是真的不会。限时结束后,学长说这个题是简单的主席树的入门题,我没学过啊。

如果你也没有学过的话,建议看我的另一篇博客,上面有自己的总结和一些博客推荐,就不用自己一个一个找了,点我进去

哦, 这个题是主席树的模板题。

代码实现

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using  namespace std;
const int maxn=1e5+7;
int root[maxn], a[maxn], x, y, k;
int n, m, cnt, tot;
struct node{
	int l, r, sum; //左子树的编号,右子树的编号,区间内的标记的数的个数
}t[maxn*40];
vector<int> v;
int getid(int x) //需要进行离散化,因为主席树叶子端点上就是一个数。
{
	return lower_bound(v.begin(), v.end(), x) - v.begin() + 1; //这里用vector进行的离散化,比较简单
}
void update(int l, int r, int &x, int y, int pos) //注意参数中的l和r是区间范围,和node里面的l和r不一样,node里面的l和r表示左右子树的编号
//这里相当于建树+更新了,有的博客还单独写了一个build函数,然后再进行update,都行。
{
	t[++cnt]=t[y];
	t[cnt].sum++;
	x=cnt;//这里是把新节点的编号记录下来
	if(l==r) return ;
	int mid=(l+r)>>1;
	if(pos<=mid)//如果需要添加的编号小于mid,就向左子树走
		update(l, mid, t[x].l, t[y].l, pos);
	else //否则就是往右子树走
		update(mid+1, r, t[x].r, t[y].r, pos);
}
int query(int l, int r, int x, int y, int k) //这里的l和r也是区间范围,x是前一个线段树
{
	if(l==r)
		return l;
	int mid=(l+r)>>1;
	int sum=t[t[y].l].sum - t[t[x].l].sum; 
	if(k<=sum)
		return query(l, mid, t[x].l, t[y].l, k);
	else 
		return query(mid+1, r, t[x].r, t[y].r, k-sum);
}
int main()
{
	scanf("%d%d", &n, &m);
	for(int i=1; i<=n; i++)
	{
		scanf("%d", &a[i]);
		v.push_back(a[i]);
	}
	sort(v.begin(), v.end()); //先排序,才能去重+离散化
	v.erase( unique( v.begin(), v.end() ) , v.end() );//去重
    tot=v.size(); //注意这里很重要,1到tot是我们主席树端点的个数。
	for(int i=1; i<=n; i++)
	{
		update(1, tot, root[i], root[i-1], getid(a[i]) );
	}
	for(int i=1; i<=m; i++)
	{
		scanf("%d%d%d", &x, &y, &k);
		printf("%d
", v[ query(1, tot, root[x-1], root[y], k) - 1]);
	}
	return 0;
 } 
欢迎评论交流!
原文地址:https://www.cnblogs.com/alking1001/p/11422955.html