昨天跟yousiki学到了整体二分!quq
最经典的就是静态区间第k小(大)
听说2013许昊然的论文-《浅谈数据结构题的几个非经典解法》 讲的特别好,从网上找了个图
复杂度很优秀的说!
具体做法一般是:
1、找到询问答案的上限和下限limit,开始二分
2、每次二分出一个mid,判断答案在l~mid之间的询问,如果有修改就判断修改的影响是否在l~mid之间,然后将l到r内的操作分成两部分,左边是l~mid之间的,右边是mid+1~r之间的
3、分别递归求解两部分的答案
4、如果二分的val值vl==vr,l~r内询问的答案已经确定,为vl,return
5、判断部分一般需要数据结构支持
这道题的做法也差不多啦,数据结构支持单点加和区间求和,用树状数组就可以
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define M 50005
#define N 100005
using namespace std;
int n,m,f[N],mx,ans[M];
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') {if(c=='-')f=-1;c=getchar();}
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
struct qwq{
int val,num;
}a[N];
inline bool cmp(qwq x,qwq y){return x.val<y.val;}
struct Query{
int cnt,k,num,l,r;
}q[M],b[M];
inline void add(int x,int y){
for(;x<=n;x+=x&-x)
f[x]+=y;
}
inline int query(int x){
int sum=0;
for(;x;x-=x&-x)
sum+=f[x];
return sum;
}
inline void calc(int vl,int vr,int l,int r){
int ll=1,rr=n+1;
while(ll<rr){//找到a数组中大于等于vl的最后一个
int mid=(ll+rr)>>1;
if(a[mid].val>=vl) rr=mid;
else ll=mid+1;
}
for(int i=rr;i<=n && a[i].val<=vr;i++)
add(a[i].num,1);//将小于等于mid的加入
for(int i=l;i<=r;i++)
q[i].cnt=query(q[i].r)-query(q[i].l-1);//计算询问在l~mid的答案
for(int i=rr;i<=n && a[i].val<=vr;i++)//记得还原
add(a[i].num,-1);
}
inline void solve(int l,int r,int vl,int vr){
if(vl==vr){
for(int i=l;i<=r;i++)
ans[q[i].num]=vl;
return;
}
int mid=(vl+vr)>>1;
calc(vl,mid,l,r);//计算询问中
int now1=l,now2=r;
for(int i=l;i<=r;i++)//将询问分成两部分
if(q[i].cnt>=q[i].k) b[now1++]=q[i];
else q[i].k-=q[i].cnt,b[now2--]=q[i];
for(int i=l;i<=r;i++) q[i]=b[i];//合并两部分
if(now1!=l) solve(l,now1-1,vl,mid);//递归求解
if(now2!=r) solve(now2+1,r,mid+1,vr);
return;
}
int main(){
n=rd(); m=rd();
for(int i=1;i<=n;i++) a[i].val=rd(),a[i].num=i;
a[n+1].val=1e9+5;
sort(a+1,a+n+1,cmp);
for(int i=1;i<=m;i++){
q[i].num=i; q[i].l=rd(); q[i].r=rd(); q[i].k=rd();
}
solve(1,m,-1e9,1e9);
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}