野生动物园(线段树+离散化)

Description
  小沐拥有一个很大的野生动物园。这个动物园坐落在一个狭长的山谷内,这个区域从西到东被划分成N个区域,每个区域都饲养着一头狮子。这些狮子从西到东编号为1,2,3,…,N。每头狮子都有一个觅食能力值Ai,Ai越小觅食能力越强。

  小沐决定对狮子进行M次投喂,每次投喂都选择一个区间[I,J],从中选取觅食能力值第K强(这里的第K强是指把A[I]...A[J]由小到大排序后的第K个)的狮子进行投喂。值得注意的是,小沐不愿意对某些区域进行过多的投喂,他认为这样有悖公平。因此小沐的投喂区间是互不包含的。你的任务就是算出每次投喂后,食物被哪头狮子吃掉了。


Input
  输入第一行有两个数N和M。此后一行有N个数,从南到北描述狮子的觅食能力值。此后M行,每行描述一次投喂。第t+2的三个数I,J,K表示在第t次投喂中,小沐选择了区间[I,J]内觅食能力值第K强的狮子进行投喂。


Output

  输出有M行,每行一个整数。第i行的整数表示在第i次投喂中吃到食物的狮子的觅食能力值。


分析:

        这个题等于是给定n个数,然后给m次询问,问i-j号数的第k小值。一看到区间查找(其实是因为正在学线段树)就想到用线段树。

        用离线算法,把所有的查询存起来,然后按照左端点从小到大排序。因为每个查询用到的是A[i-j],而一般的线段树维护集合查询第K小数难以在规定区间[i-j]同时查询,所以必须把A[1]-A[i-1]删掉,A[i]-A[j]加入,又因为是按照左端点排序的,所以不用担心j右边。注意A数组需要先离散化为B数组。

        枚举到一个查询时,把A[i]之前的数字删掉,A[i-j]的数字添加进集合,然后查询,记录答案即可。最后再按照输入顺序排序,输出答案。



#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100005;
const int maxm=50005;
int n,m,np=0,rt=0,tot=0,lc[maxn*2],rc[maxn*2],num[maxn*2],B[maxn],A[maxn];
struct data{int i,j,k,id,ans;}kk[maxm];
bool cmp1(data a,data b) {return a.i<b.i;}
bool cmp2(data a,data b) {return a.id<b.id;}

void ready()
{
	for(int i=1;i<=n;i++) 
	{
		scanf("%d",&A[i]);
		B[++tot]=A[i];
	}
	sort(B+1,B+1+tot);
	tot=unique(B+1,B+1+tot)-B-1;
	
	for(int t=1;t<=m;t++) 
	{
		scanf("%d%d%d",&kk[t].i,&kk[t].j,&kk[t].k);
		kk[t].id=t;
	}
	sort(kk+1,kk+1+m,cmp1);
}

void pushup(int now,int L,int R)
{
	num[now]=num[L]+num[R];
}

void Build(int &now,int L,int R)
{
	now=++np;
	if(L==R) return;
	int m=(L+R)/2;
	Build(lc[now],L,m);
	Build(rc[now],m+1,R);
}

void Update(int now,int L,int R,int x,int d)
{
	if(L==R) 
	{
		num[now]+=d;
		return;
	}
	int m=(L+R)/2;
	if(x<=m) Update(lc[now],L,m,x,d);
	else Update(rc[now],m+1,R,x,d);
	pushup(now,lc[now],rc[now]);
}

int Kth(int now,int L,int R,int x)
{
	if(L==R) return L;
	int m=(L+R)/2,t=num[lc[now]];
	if(x<=t) return Kth(lc[now],L,m,x);
	return Kth(rc[now],m+1,R,x-t);
}

int main()
{
//	freopen("in.txt","r",stdin);
	scanf("%d%d",&n,&m);
	ready();//离散化 
	Build(rt,1,tot);
	int x=1,y=1;
	for(int t=1;t<=m;t++)
	{
		for(;x<kk[t].i;x++)//A[1]-A[i-1] 删掉 
		{
			int v=lower_bound(B+1,B+1+tot,A[x])-B;
			Update(rt,1,tot,v,-1);
		}
		for(;y<=kk[t].j;y++)//A[i]-A[j]加入 
		{
			int v=lower_bound(B+1,B+1+tot,A[y])-B;
			Update(rt,1,tot,v,1);
		}
		kk[t].ans=Kth(rt,1,tot,kk[t].k);
	}
	sort(kk+1,kk+1+m,cmp2);//按照输入顺序排序 
	for(int i=1;i<=m;i++) printf("%d\n",B[kk[i].ans]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/wwwengine/article/details/80978515