还是先描述一下题意:
给出一个长度为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; }