学习笔记:可持久化线段树(主席树)

参考文章来源

参考代码

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 200005;

int n,m,li,ri,ki,length,nid;
//初始数字个数;询问个数;询问左右区间;第k大的数;离散化后数列长度;所有线段树的所有节点的序号。
int a[MAXN],b[MAXN];
//原数列和离散化后的数列。
int Root[MAXN * 40],lson[MAXN * 40],rson[MAXN * 40],sum[MAXN * 40];
//主席树(Root[]数组)的每一个节点(数组元素)都是一棵线段树的根节点(代表1~n的区间)。
//一般而言,主席树要开40倍的数组。

namespace ChairmanTree
{
	void buildTree(int l,int r,int &cur) //使用引用传递参数,得以给每个节点编号。
	{
		cur = ++nid;
		sum[cur] = 0;
		if (l == r) return; //已经到达叶子节点,返回。注意这句话要写在上面的初始化下面。
		int mid = (l + r) >> 1;
		buildTree(l,mid,lson[cur]); //相同子问题。
		buildTree(mid + 1,r,rson[cur]);
	}
	
	inline void update(int l,int r,int &cur,int pre,int pos)
	{
		cur = ++nid;
		lson[cur] = lson[pre];
		rson[cur] = rson[pre]; //初始化,之后左右子节点的标号可能被修改。
		sum[cur] = sum[pre] + 1; //加入了一个新数,那么这个数所在的区间sum值就要增加。
		if (l == r) return;
		int mid = (l + r) >> 1;
		if (pos <= mid) update(l,mid,lson[cur],lson[pre],pos);
		else update(mid + 1,r,rson[cur],rson[pre],pos);
		//假如执行了else语句,看起来rson[cur]和rson[pre]是一样的,但进入
		//下一层递归后,rson[cur]就变成了++nid,就是相当于新开了一个节点。
	}
	
	inline int query(int L,int R,int l,int r,int k)
	{
		if (l == r) return l;
		int mid = (l + r) >> 1;
		int lcnt = sum[lson[R]] - sum[lson[L]]; //统计左子树的数字个数。假如要问区间第4大,左子树
												//一共只有3个数,那么这个第4大就一定在右子树了。
		if (k <= lcnt) return query(lson[L],lson[R],l,mid,k);
		else return query(rson[L],rson[R],mid + 1,r,k - lcnt);
	}
}

void init()
{
	freopen("in.txt","r",stdin);
	scanf("%d%d",&n,&m);
	for (int i = 1;i <= n;++i)
	{
		qread(a[i]);
		b[i] = a[i];
	}
	sort(b + 1,b + n + 1); //数组是1基准的。
	length = unique(b + 1,b + n + 1) - (b + 1);
	ChairmanTree::buildTree(1,length,Root[0]);
	for (int i = 1;i <= n;++i)
	{
		a[i] = lower_bound(b + 1,b + length + 1,a[i]) - b;
		//lower_bound()返回的是迭代器,还要减去b数组首地址来得到元素下标。
	}
	for (int i = 1;i <= n;++i)
	{
		ChairmanTree::update(1,length,Root[i],Root[i - 1],a[i]);
	}
}

void work()
{
	for (int i = 1;i <= m;++i)
	{
		qread(li);
		qread(ri);
		qread(ki);
		int ans = ChairmanTree::query(Root[li - 1],Root[ri],1,length,ki);
		printf("%d\n",b[ans]);
	}
}

int main()
{
	init();
	work();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_37661548/article/details/87089073