我们开门见山,讲讲一道sb题:

给你一个数组,查这个数组的第x大元素。

排序?可以

二分?怎么做啊?

二分出一个mid,判断这个数组中有多少个数小于等于mid,如果个数大于等于x,就递归到[l,mid]区间,否则是[mid+1,r]区间,这样递归下去就能得到结果。

怎么计算小于等于mid的个数?

for一遍原数组?其实只要存储数组中值在[l,r]中的数,然后计算就好,而对于[1,l-1],可以预先存储。

怎么计算?树状数组,范围太大就离散化。

这样很麻烦吧?排序更好吧?

但是当我给你多个询问时,你就不会这么觉得了。

【算法学习】整体二分

回顾刚才的过程,我们做上图的类比。

把询问看成球,答案的区间就是这个矩形,每一次我们将上一层的球判断它会掉到下层的哪一块中,掉到底层就是获得了答案。

刚刚的过程就是球只有1个的情况。

球有多个时,就是整体二分!

就是对于当前的mid,对每一个询问都计算答案是大了还是小了,然后决定分配到下一层的哪边。

要注意保证时间复杂度哦!

一道例题:HDU 5412

查询区间K小,要支持单点修改。

看代码:

 1 #include<cstdio>
 2 int n,q,a[100001],sum[300001],ans[300001],que1[300001],que2[300001],bit[300001];
 3 int I[300001],type[300001],ql[300001],qr[300001],k[300001],cnt;
 4 inline void ins(int t,int l,int r,int x){type[++cnt]=t,ql[cnt]=l,qr[cnt]=r,k[cnt]=x,I[cnt]=cnt;}
 5 inline void Ins(int i,int x){for(;i<=n;bit[i]+=x,i+=i&-i);}
 6 inline int Qur(int i){int Sum=0;for(;i;Sum+=bit[i],i-=i&-i);return Sum;}
 7 void divide(int l,int r,int low,int upp){
 8     if(l>r) return;
 9     if(low==upp) {for(int i=l;i<=r;++i) if(type[I[i]]==3) ans[I[i]]=low; return;}
10     int mid=low+upp>>1,cl=0,cr=0;
11     for(int i_=l,i;i_<=r;++i_){
12         i=I[i_];
13         if(type[i]==1) {if(k[i]<=mid) Ins(ql[i],1), que1[++cl]=i; else que2[++cr]=i;}
14         if(type[i]==2) {if(k[i]<=mid) Ins(ql[i],-1), que1[++cl]=i; else que2[++cr]=i;}
15         if(type[i]==3){
16             int tmp=Qur(qr[i])-Qur(ql[i]-1);
17             if(k[i]<=sum[i]+tmp) que1[++cl]=i;
18             else que2[++cr]=i, sum[i]+=tmp;
19         }
20     }
21     for(int i_=l,i;i_<=r;++i_){
22         i=I[i_];
23         if(type[i]==1) if(k[i]<=mid) Ins(ql[i],-1);
24         if(type[i]==2) if(k[i]<=mid) Ins(ql[i],1);
25     }
26     for(int i=1;i<=cl;++i) I[l+i-1]=que1[i];
27     for(int i=1;i<=cr;++i) I[l+cl+i-1]=que2[i];
28     divide(l,l+cl-1,low,mid); divide(l+cl,r,mid+1,upp);
29 }
30 int main(){
31     scanf("%d",&n);
32     for(int i=1;i<=n;++i) scanf("%d",a+i), ins(1,i,i,a[i]);
33     scanf("%d",&q);
34     for(int i=1,t,x,y,z;i<=q;++i){
35         scanf("%d",&t);
36         if(t==1) scanf("%d%d",&x,&y), ins(2,x,x,a[x]), ins(1,x,x,y), a[x]=y;
37         else scanf("%d%d%d",&x,&y,&z), ins(3,x,y,z);
38     }
39     divide(1,cnt,1,1000000000);
40     for(int i=1;i<=cnt;++i) if(ans[i]) printf("%d\n",ans[i]);
41     return 0;
42 }
View Code

相关文章: