整体二分初步——静态区间第k大

Description

给定一个长度为n的序列,m个询问,每个询问的形式为:L,r,k表示在[L,r]间中的第k大元素。

Input

第1行:2个数,n,m表示序列的长度和询问的个数
第2行:n个数,表示n个数的大小
第3-m+2行:每行3个数,L,r,k表示询问在[L,r]区间内第k小的元素

Output

对于每个询问,输出答案。

Sample Input

7 2

1 5 2 6 3 7 4

1 5 3

2 7 1

Sample Output

3

2

Hint

【数据范围】
  对于100%的数据,n<=100000, m<=100000,1<=L<=r<=n, 1<=k<=r-L+1

整体二分,将所有询问放在一起二分答案,题目必须满足可二分性质,鉴于目前只做了这道题,因此还不理解修改贡献之类的东西。。。反正重要的是贡献与询问之间是独立的(说不太清楚= =)

说下大概过程,我们二分答案,将满足条件的放左边,不满足的放右边,直到到底就记录答案。

重点是如何计算贡献,长度必须和当前操作有关,这个就要考验技术了。

#include
using namespace std;
const int Maxn=200005;
struct Inter{
	int val,pos;
	bool operator<(const Inter&rhs)const{
		return val0;x-=lowbit(x))ret+=c[x];
		return ret;
	}
}bit;
int Work(int l,int r,int x,int tl,int tr,int &pos){
	int L=tl,R=tr;
	while(L<=R){
		int Mid=L+R>>1;
		if(a[Mid].val<=x)pos=Mid,L=Mid+1;
		else R=Mid-1;
	}
	for(int i=tl;i<=pos;++i)
		bit.add(a[i].pos,1);
	int i=l,j=r;
	for(int k=l;k<=r;++k){
		int tmp=bit.sum(q[k].r)-bit.sum(q[k].l-1);
		if(tmp>=q[k].k)t[i++]=q[k];
		else q[k].k-=tmp,t[j--]=q[k];
	}
	int k=l-1,ret=i-1;
	while(--i>=l)q[++k]=t[i];
	while(++j<=r)q[++k]=t[j];
	for(int i=tl;i<=pos;++i)
		bit.add(a[i].pos,-1);
	return ret;
}
void Divide(int l,int r,int L,int R,int tl,int tr){
	//l~r询问序列  L~R二分答案序列  tl~tr当前贡献序列
	if(l>r)return ;
	if(L==R){
		for(int i=l;i<=r;++i)
			ans[q[i].idx]=v[L];
		return ;
	}
	int Mid=L+R>>1,pos;//在元素中二分,pos绝对合法 
	int t=Work(l,r,v[Mid],tl,tr,pos);
	Divide(l,t,L,Mid,tl,pos),Divide(t+1,r,Mid+1,R,pos+1,tr);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%d",&v[i]);
		a[i]=(Inter){v[i],i};
	}
	sort(a+1,a+n+1);
	sort(v+1,v+n+1);
	mx=unique(v+1,v+n+1)-v-1;
	for(int i=1;i<=m;++i){
		int l,r,k;scanf("%d%d%d",&l,&r,&k);
		q[i]=(Query){l,r,k,i};
	}
	Divide(1,m,1,mx,1,n);
	for(int i=1;i<=m;++i)
		cout<

 

你可能感兴趣的:(离线,整体二分)