给定一个长度为n的序列,m个询问,每个询问的形式为:L,r,k表示在[L,r]间中的第k大元素。
第1行:2个数,n,m表示序列的长度和询问的个数
第2行:n个数,表示n个数的大小
第3-m+2行:每行3个数,L,r,k表示询问在[L,r]区间内第k小的元素
对于每个询问,输出答案。
7 2
1 5 2 6 3 7 4
1 5 3
2 7 1
3
2
【数据范围】
对于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<