Link
Solve
这道题有很多种做法,我用的是离线分治算法
设序列(A)中最小的数为(MINA),最大的数为(MAXA),我们尝试在值域([MINA,MAXA])上进行二分答案,设第一次二分的值为(mid).
在二分的过程中,扫描每个询问,统计在下标区间([l,r])中不大于(mid)的数有多少个,记为(c_i),对于每个([l_i,r_i]),(mid)都是一个固定的值,所以可以用树状数组来线性的求出每个(c_i)
然后我们对询问进行分类:
1.若(k_i≤c_i)则说明第(i)个询问的答案在([MINA,mid])中。
2.若(k_i>c_i),则说明第(i)个询问的答案在([mid+1,MAXA])中,并且等价于在值域([mid+1,MAXA])中的限制下找第(k_i-c_i)小的数。
我们把序列(A)中的数也进行分类,不大于(mid)的数构成一个子序列(LA),大于(mid)的数构成一个子序列(RA)。
于是我们就可以把询问和数值分成两类,并且两类互不相关。
我们就得到了一个分治算法,设(solve(L,R,a,q)),表示有一个值域为([L,R])的整数序列(a),询问序列(q)((q)中存储个若干个三元组(l_i,r_i,k_i),表示求(a)的下标区间([l_i,r_i])中第(k_i)小的数。
1.设(mid=(L+R)>>1)
2.利用树状数组,对于(q)中每个询问,统计在序列(a)下标区间([l_i,r_i])中不大于(mid)的数有多少个,记为(c_i).
3.若(k_i≤c_i),则把该询问加入到(lq)中,否则令(k_i-=c_i),将其加入到序列(rq)中。
4.令序列(a)中(≤mid)的数构成序列(la),(>mid)的数构成序列(ra)。
5.递归求解(solve(L,mid,la,lq))和(solve(mid+1,R,ra,rq))。
递归的边界为:当(q)为空时,直接返回。当(L==R)时,说明询问序列中的每个问题L就是答案(R也一样),统计后返回
在实际做的时候我们把(lq)和(la)放在一个序列里面做,打一个(op)标记判断一下,因为(la,lq)和(ra,rq)是互不干扰的。合并序列时直接在原序列上修改,然后记下范围指针就好了
因为分治最多进行(logSIZE)层((SIZE)为值域大小),在算上树状数组的(log),整个事件复杂度为(O((N+M)logSIZElogN)),如果加上离散化,可以做到(O((N+M)log^2N))
Code
#include<bits/stdc++.h>
using namespace std;
int N,M,tot;
const int maxn=200005,INF=1<<30;
struct AS{
int x,y,z,op;
}q[maxn<<1],lq[maxn<<1],rq[maxn<<1];
int ans[maxn],c[maxn];
inline int read(){
int ret=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
inline void add_x(int x,int val){
for(int i=x;i<=N;i+=i&-i)c[i]+=val;
return ;
}
inline int get(int x){
int ret=0;
for(int i=x;i;i-=i&-i)ret+=c[i];
return ret;
}
void solve(int lval,int rval,int st,int ed){
if(st>ed)return ;
if(lval==rval){
for(int i=st;i<=ed;i++){
if(q[i].op>0)ans[q[i].op]=lval;
}
return ;
}
int mid=lval+rval>>1;
int lt=0,rt=0;
for(int i=st;i<=ed;i++){
if(q[i].op==0){
if(q[i].y<=mid)add_x(q[i].x,1),lq[++lt]=q[i];
else rq[++rt]=q[i];
}
else {
int cnt=get(q[i].y)-get(q[i].x-1);
if(q[i].z<=cnt)lq[++lt]=q[i];
else q[i].z-=cnt,rq[++rt]=q[i];
}
}
for(int i=ed;i>=st;i--){
if(q[i].op==0&&q[i].y<=mid)add_x(q[i].x,-1);
}
for(int i=1;i<=lt;i++)q[st+i-1]=lq[i];
for(int i=1;i<=rt;i++)q[st+lt+i-1]=rq[i];
solve(lval,mid,st,st+lt-1);
solve(mid+1,rval,st+lt,ed);
return ;
}
int main(){
N=read();M=read();
for(int i=1;i<=N;i++){
++tot;
q[tot].op=0;q[tot].x=i;q[tot].y=read();
}
for(int i=1;i<=M;i++){
++tot;
q[tot].op=i;q[tot].x=read(),q[tot].y=read(),q[tot].z=read();
}
solve(-INF,INF,1,tot);
for(int i=1;i<=M;i++)printf("%d
",ans[i]);
return 0;
}