POJ2104区间第k小

题意,给你一个数字序列和一堆询问,问你[l~r]里第k小的数

线段树,呸,主席树的入门板题,但是我还是学了半天QAQ

先说个大暴力,对于每个询问区间,我们排序,然后找第k个数,emm,复杂度爆表

然后貌似线段树可行??脑补一下,对于每个询问建一棵树,emm,貌似比暴力复杂度还高??

欸,所以我们引入了主席树,主席树呢,用到了前缀和的思路,我们通过前缀和sum[l~r]=sum[1~r]-sum[1~l-1]可以获得一些灵感,求区间第k小也可以这么做

/*下面开始引用

/*假设我们知道[1, l-1]之间有多少个数比第k小的数小,那么我们只要减去这些数之后在[1, r]区间内第k小的数即是[l, r]区间内的第k小数

更确切的说,我们要求[l, r]区间内的第k小数  可以 用以[1, r]建立的线段树去减去以[1, l-1] 建立的线段树

这样能够减的条件是这两棵树必须是同构的。

若是不太明白, 我们来举个例子:

如有序列  1 2 5 1 3 2 2 5 1 2

我们要求 [5,10]第5小的数

(数列中不存在4、6、7、8 但根据原理就都写出来了,为方便理解,去掉了hash的步骤,实际的代码中其实只要一棵4个叶子节点的树即可)

(红色的为个数)

我们建立的[1, l-1] (也就是[1, 4])之间的树为*/

[1, r]也就是[1, 10]的树为


两树相减得到

POJ2104区间第k小_第1张图片

/*我们来找第5小的数:

发现左子树为5  所以第5小的数在左边, 再往下(左4右1) 发现左边小于5了 ,所以第5小的数在右边 所以第5小的数就是3了

 

同样的,我们只要建立[1, i] (i是1到n之间的所有值)的所有树,每当询问[l, r]的时候,只要用[1, r]的树减去[1, l-1]的树,再找第k小就好啦

我们将这n个树看成是建立在一个大的线段树里的,也就是这个线段树的每个节点都是一个线段树( ——这就是主席树)

最初所有的树都是空树,我们并不需要建立n个空树,只要建立一个空树,也就是不必每个节点都建立一个空树

插入元素时,我们不去修改任何的结点,而是返回一个新的树( ——这就是函数式线段树)

因为每个节点都不会被修改,所以可以不断的重复用,因此插入操作的复杂度为O(logn)

总的复杂度为O((n+m)lognlogN)   (听说 主席树的芭比说 加上垃圾回收, 可以减少一个log~~~ 然而这只是听说)*/

引用完毕 原文

代码

//By AcerMo
#include
#include
#include
#include
#include
using namespace std;
const int M=2000500;
int n,m,cnt;
int now[M],root[M];//离散化后的位置,树的根
struct Segt
{
	int bo[2];
	int sum;
	Segt(){sum=0;}
}t[M];//线段树
struct past
{
	int val,id;
}num[M];//存储数列
bool cmp(past a,past b)
{
	return a.val>1;
	if (val<=mid) update(val,t[rt].bo[0],l,mid);//然后考虑放到哪里
	else update(val,t[rt].bo[1],mid+1,r);
	return ;
}//构树
int query(int x,int y,int k,int l,int r)
{
	int d=t[t[y].bo[0]].sum-t[t[x].bo[0]].sum;
	if (l==r) return l;
	int mid=(l+r)>>1;
	if (k<=d) return query(t[x].bo[0],t[y].bo[0],k,l,mid);
	else return query(t[x].bo[1],t[y].bo[1],k-d,mid+1,r);
}//按照刚刚的公式
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
		scanf("%d",&num[i].val),num[i].id=i;
	sort(num+1,num+n+1,cmp);
	for (int i=1;i<=n;i++)
		now[num[i].id]=i;
	constt();
	for (int i=1;i<=n;i++)
	{
		root[i]=root[i-1];
		update(now[i],root[i],1,n);
	}//构树
	int l,r,kth;
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&l,&r,&kth);
		int ans=num[query(root[l-1],root[r],kth,1,n)].val;//公式
		printf("%d\n",ans);
	}
	return 0;
}

你可能感兴趣的:(数据结构-主席树)