poj 2104 归并树

每次查找二分出某个数,logn时间在树中找到它的区间,二分找比它小的数的个数,三次二分= (log n)^3

解决的问题:查询某个区间内第k大的数

值得注意的是:

1.归并树,划分树都只能解决元素不重复的情况

2.多个x有相同rank的时候取最大的x

这些相同rank的数组成一个序列A1,A2,……An,之间两两不同,因为没有重复元素

若答案不是An而是Ai(1<=i<n),则An的rank应该是rank(An)+1,这与已知矛盾



#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define N 100500
#define M 5500
struct node
{
	int l,r,rank;
}tree[3*N];
int ha[20][N];
int n,m;
void build(int pos,int l,int r,int rank)
{
	tree[pos].rank=rank;
	tree[pos].l=l;tree[pos].r=r;
	if(l==r)
	{
		ha[rank][l]=ha[0][l];
		return;
	}
	int mid=(l+r)>>1;
	build(pos*2,l,mid,rank+1);
	build(pos*2+1,mid+1,r,rank+1);
	int i=l,j=mid+1,cnt=l;
	while(i<=mid&&j<=r)
	{
		if(ha[rank+1][i]<ha[rank+1][j])
			ha[rank][cnt++]=ha[rank+1][i++];
		else ha[rank][cnt++]=ha[rank+1][j++];
	}
	while(i<=mid) ha[rank][cnt++]=ha[rank+1][i++];
	while(j<=r) ha[rank][cnt++]=ha[rank+1][j++];
}

int query(int pos,int l,int r,int x)
{
	if(tree[pos].l==l&&tree[pos].r==r)
	{
		int num=lower_bound(ha[tree[pos].rank]+tree[pos].l,ha[tree[pos].rank]+tree[pos].r+1,x)-(ha[tree[pos].rank]+tree[pos].l);
		return num;
	}
	int mid=(tree[pos].l+tree[pos].r)>>1;
	int c1=0,c2=0;
	if(r<=mid)
		c1=query(pos*2,l,r,x);
	else if(l>mid)
		c2=query(pos*2+1,l,r,x);
	else
	{
		c1=query(pos*2,l,mid,x);
		c2=query(pos*2+1,mid+1,r,x);
	}
	return c1+c2;
}

int main ()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		for(int i=1;i<=n;++i)
			scanf("%d",&ha[0][i]);
		build(1,1,n,0);
		int ll,rr,k;
		while(m--)
		{
			scanf("%d%d%d",&ll,&rr,&k);
			int l=1,r=n,mid;int res,re;
			while(l<=r)
			{
				mid=(l+r)/2;
				res=query(1,ll,rr,ha[0][mid])+1;
				if(res<=k)
				{
					re=mid;
					l=mid+1;
				}
				else r=mid-1;  // 为了返回有相同排位的数中最大的一个,二分细节中很多要注意
			}
	
			printf("%d\n",ha[0][re]);
		}
	}
	//system("pause");
	return 0;
}


你可能感兴趣的:(poj 2104 归并树)