整体二分
今天刚透彻了整体二分,写篇blog;
由于整体二分比较难以口述,来道例题讲解
静态区间第k小
同时也是主席树模板题,可是主席树模板怎么能用主席树写呢
往下看,不会主席树的也可以水过这道紫体题了;
首先设A序列表示表示给定序列;
所有询问的答案一定在MINA~MAXA之间;
对MINA~MAXA进行二分答案;
二分答案一个询问想必大家都会,单数这道题时间不允许;
考虑对整体进行二分,对于所有询问二分;
也就是对询问进行分类,考虑二分出的答案mid;
用树状数组维护小于等于mid的个数;
对于每个区间ask(r)-ask(l-1)可以知道在这个询问区间里小于等于mid的个数;
就可以把所有询问分为两类,设num为l~r里小于等于mid的个数;
第一类:num<=k 第二类num>k;
考虑第一类询问的答案一定在L~mid之间;
第二类询问的答案一定在mid+1~R之间;
L,R表示当前的答案区间;
那么就可以把询问分成两类在分别将行分治
知道L==R为止,L就是答案;
注意A序列也要分开;
考虑对于第一类询问,A序列中>mid的值对这些区间没有影响
所以可以直接舍弃;
对于第二类询问,它们的答案一定在mid+1~r之间
所以只考虑>mid的值;
例题
#include<iostream>
#include<cstdio>
using namespace std;
const int N=2e5+7;
const int inf=1e9;
struct node{
int op,x,y,z;
}q[N<<1],ql[N<<1],qr[N<<1];
int n,m,cnt;
int ans[N],t[N];
int lowbit(int x){
return x&-x;
}
void change(int x,int val){
for(;x<=N-7;x+=lowbit(x)){
t[x]+=val;
}
}
int ask(int x){
int res=0;
for(;x;x-=lowbit(x)){
res+=t[x];
}
return res;
}
void work(int L,int R,int l,int r){
if(l>r) return;
if(L==R){
for(int i=l;i<=r;i++){
if(q[i].op>0) ans[q[i].op]=L;
}
return;
}
int mid=(L+R)>>1;
int tl=0,tr=0;
for(int i=l;i<=r;i++){
if(q[i].op==0){//属于A序列
if(q[i].y<=mid) change(q[i].x,1),ql[++tl]=q[i];
else qr[++tr]=q[i];
} else {//属于询问区间
int tt=ask(q[i].y)-ask(q[i].x-1);
if(tt>=q[i].z) ql[++tl]=q[i];
else {
q[i].z-=tt;//减去tt;
qr[++tr]=q[i];
}
}
}
for(int i=r;i>=l;i--){
if(q[i].op==0&&q[i].y<=mid) change(q[i].x,-1);//清空树状数组;
}
for(int i=1;i<=tl;i++) q[l+i-1]=ql[i];
for(int i=1;i<=tr;i++) q[l+tl+i-1]=qr[i];
work(L,mid,l,l+tl-1);
work(mid+1,R,l+tl,r);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&q[++cnt].y);
q[cnt].op=0;q[cnt].x=i;//op分类
}
for(int i=1;i<=m;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
q[++cnt].x=x;
q[cnt].y=y;
q[cnt].z=z;
q[cnt].op=i;
}
work(-inf,inf,1,cnt);
for(int i=1;i<=m;i++) cout<<ans[i]<<"
";
}