【POJ 2104】【主席树模板题】K-th Number

题意:

      静态询问区间第K大问题。给出一个数组,然后多次询问某一区间第K大数是多少。

思路:

      典型的主席树模板题。

      所以就大致讲一下静态主席树的原理。

      先回顾一下权值线段树,每个节点维护一个sum,表示数组中有多少个点在这个节点所代表的左右区间内,这样就可以求出全局第k大问题。

      那么主席树就是建立多棵线段树,第i个线段树维护了 [1-i] 区间内的权值线段树,那么当求 [l,r] 这个区间内的第k大时,就只需要考虑 第l-1 和 第r 棵线段树,将这两个线段树内相同节点的sum相减,就是[l,r]这个区间内的信息了。

      然后因为第i棵线段树和第i+1棵线段树中有大量公用部分,所以每多建一棵线段树,只需要多开logn个节点,实现了空间优化。

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define rep(i,a,b) for(int i = a; i <= b; i++)
using namespace std;
const int N = 1e5+100;

struct Tree{
	int lc,rc; //左右节点 t数组 编号
	int l,r; //节点的左右端点
	int sum; 
}t[N*20];
int n,m,a[N],num,b[N],root[N],tot;

int build(int l,int r)
{
	int p = ++tot; // 新建一个节点,编号为p,代表当前区间[l,r]
	t[p].l = l, t[p].r = r, t[p].sum = 0;
	if(l == r) return p;
	int mid = (l+r)>>1;
	t[p].lc = build(l,mid);
	t[p].rc = build(mid+1,r);
	return p;
}

int insert(int now,int pos,int k)
{
	int p = ++tot;
	t[p] = t[now]; //建立副本
	if(t[p].l == t[p].r){
		t[p].sum += k; //在副本上修改
		return p;
	}
	int mid = (t[p].l+t[p].r)>>1;
	if(pos <= mid) t[p].lc = insert(t[p].lc,pos,k); //保留右儿子部分,把左儿子更新
	else t[p].rc = insert(t[p].rc,pos,k);
	t[p].sum = t[t[p].lc].sum + t[t[p].rc].sum;
	return p;
}

int ask(int lp,int rp,int k) //lp和rp所代表的区间是相同的,他们只不过是在不同状态下的副本
{
	if(t[lp].l == t[lp].r) return t[lp].l; //找到答案
	int cnt = t[t[rp].lc].sum-t[t[lp].lc].sum; // 值在[l,mid]中的数有多少个
	if(cnt >= k) return ask(t[lp].lc,t[rp].lc,k);
	else return ask(t[lp].rc,t[rp].rc,k-cnt);
}

int main()
{
	num = tot = 0;
	scanf("%d%d",&n,&m);
	rep(i,1,n){
		scanf("%d",&a[i]);
		b[++num] = a[i];
	}
	sort(b+1,b+1+num); //离散化
	num = unique(b+1,b+1+num)-b-1;
	root[0] = build(1,num); //root[0]这颗树是一棵空树,关于离散化后的值域建树
	rep(i,1,n)
	{
		int x = lower_bound(b+1,b+1+num,a[i])-b; //离散化后的值
		root[i] = insert(root[i-1],x,1); //值为x的数增加1个
	}
	rep(i,1,m)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		int ans = ask(root[x-1],root[y],z);
		printf("%d\n",b[ans]); //从离散化后的值变回原值
	}
	return 0;
	//root[i]:表示只考虑1-i这些数时候建树的情况,这颗树树根的编号
}

/*
新建多个权值线段树副本,记录只考虑1-i个数时,每个数出现在各个区间的个数是多少,类似于建多棵权值线段树
然后第i棵线段树,参考第i-1棵线段树,优化空间
空间为4*n*log(n)
*/

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/83242064