还是先描述一下题意:
给出一个长度为n的数列,m次询问区间内的第k大数
对划分树,主席树和整体二分通过这题做了一下比较
划分树 1000ms+
主席树 2000ms+
整体二分 1500ms+
整体二分介于两者之前,对于这题复杂度约莫是O( (n+m)log(n+m)log( Range( ans ) ) )
整体二分这个东西比较奇妙,运用的是离线算法,而主席树和划分树都是在线的
先引用一下2013年许昊然论文-《浅谈数据结构题的几个非经典解法》解释一下整体二分
此题整体二分思路:
1.确定答案在l~r这个区间内
2.取二分中值mid,询问所有查询操作在数组中小于等于mid的情况下,有多少个数在查询区间内
3.由此将查询分为两类
q1: 区间内个数大于等于k
q2:区间内个数小于k
可以看出q1情况下的查询应该缩小答案,q2情况下的查询应该放大答案,
同时q2情况下记录mid对对答案的影响值cur(有点类似于cdq分治思想)
由此为依据对数组值和查询操作一起进行二分,回到步骤1一直到得到所有答案
此处统计个数用树状数组简洁方便
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<string>
#include<iostream>
using namespace std;
#define INF 0x3f3f3f3f
struct node
{
int l,r,k,val;
int cur,index;
int kind;
} q[200005],q1[100005],q2[100005];
int n,m;
int ans[100006];
int c[100006];
int tmp[200006];
void init()
{
memset(c,0,sizeof c);
memset(tmp,0,sizeof tmp);
for(int i=1; i<=m+n; i++)
{
q[i].cur=q1[i].cur=q2[i].cur=0;
}
}
inline int lowbit(int x)
{
return x&-x;
}
inline void update(int x,int val)
{
for(; x<=n; x+=lowbit(x)) c[x]+=val;
}
inline int query(int x)
{
int sum=0;
for(; x>0; x-=lowbit(x)) sum+=c[x];
return sum;
}
void divide(int s,int t,int l,int r)
{
if(s>t) return ;
if(l==r)
{
for(int i=s; i<=t; i++)
if(q[i].kind==2) ans[q[i].index]=l;
return ;
}
int mid=(l+r)>>1;
int num1=0,num2=0,flag1=0,flag2=0;
for(int i=s; i<=t; i++)
{
if(q[i].kind==1)
{
if(q[i].val<=mid) update(q[i].index,1),q1[num1++]=q[i];
else q2[num2++]=q[i];
}
else if(q[i].kind==2)
{
tmp[i]=query(q[i].r)-query(q[i].l-1);
if(q[i].cur+tmp[i]>=q[i].k) q1[num1++]=q[i],flag1=1;
else q[i].cur+=tmp[i],q2[num2++]=q[i],flag2=1;
}
}
for(int i=s; i<=t; i++)
{
if(q[i].kind==1&&q[i].val<=mid) update(q[i].index,-1);
}
for(int i=0; i<num1; i++) q[s+i]=q1[i];
for(int i=0; i<num2; i++) q[s+num1+i]=q2[i];
if(flag1) divide(s,s+num1-1,l,mid);
if(flag2) divide(s+num1,t,mid+1,r);
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
int cnt=1;
for(int i=1; i<=n; i++)
{
scanf("%d",&q[cnt].val);
q[cnt].kind=1;
q[cnt].index=i;
cnt++;
}
for(int i=1; i<=m; i++)
{
q[cnt].index=i;
scanf("%d%d%d",&q[cnt].l,&q[cnt].r,&q[cnt].k);
q[cnt].kind=2;
cnt++;
}
divide(1,cnt-1,-INF,INF);
for(int i=1; i<=m; i++) printf("%d\n",ans[i]);
}
return 0;
}