zoukankan      html  css  js  c++  java
  • 【算法学习】整体二分

    我们开门见山,讲讲一道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
    ",ans[i]);
    41     return 0;
    42 }
    View Code

    一道例题:洛谷 P3332。

    查询区间K大,要支持区间加入。

    看代码:

     1 #include <cstdio>
     2 
     3 typedef long long LL;
     4 const int MN = 50005;
     5 
     6 int N, Q;
     7 
     8 int opt[MN], L[MN], R[MN]; LL V[MN];
     9 int P[MN];
    10 int s1[MN], s2[MN], t1, t2;
    11 
    12 LL Sum[MN];
    13 int Ans[MN];
    14 
    15 LL b1[MN], b2[MN];
    16 inline void Add(LL *b, int i, LL x) { for(; i <= N; i += i & -i) b[i] += x; }
    17 inline LL Qur(LL *b, int i) { LL A = 0; for(; i; i -= i & -i) A += b[i]; return A; }
    18 inline void Add(int l, int r, LL x) {
    19     ++r;
    20     Add(b1, l,  x), Add(b2, l,  (l - 1) * x);
    21     Add(b1, r, -x), Add(b2, r, -(r - 1) * x);
    22 }
    23 inline LL Qur(int l, int r) {
    24     --l;
    25     return r * Qur(b1, r) - Qur(b2, r) - l * Qur(b1, l) + Qur(b2, l);
    26 }
    27 
    28 void Solve(int l, int r, int lb, int rb) {
    29     if (lb == rb) {
    30         for (int i = l; i <= r; ++i)
    31             if (opt[P[i]] == 2)
    32                 Ans[P[i]] = lb;
    33         return ;
    34     }
    35     int mid = lb + rb >> 1;
    36     t1 = t2 = 0;
    37     for (int i = l; i <= r; ++i) {
    38         if (opt[P[i]] == 1) {
    39             if (V[P[i]] <= mid)
    40                 s1[++t1] = P[i];
    41             else {
    42                 Add(L[P[i]], R[P[i]], 1);
    43                 s2[++t2] = P[i];
    44             }
    45         }
    46         else {
    47             LL num = Sum[P[i]] + Qur(L[P[i]], R[P[i]]);
    48             if (num >= V[P[i]]) 
    49                 s2[++t2] = P[i];
    50             else {
    51                 Sum[P[i]] = num;
    52                 s1[++t1] = P[i];
    53             }
    54         }
    55     }
    56     for (int i = l; i <= r; ++i) {
    57         if (opt[P[i]] == 2) continue;
    58         if (V[P[i]] > mid)
    59             Add(L[P[i]], R[P[i]], -1);
    60     }
    61     int pos = l;
    62     for (int i = 1; i <= t1; ++i)
    63         P[pos++] = s1[i];
    64     for (int i = 1; i <= t2; ++i)
    65         P[pos++] = s2[i];
    66     int M = l + t1 - 1;
    67     Solve(l, M, lb, mid);
    68     Solve(M + 1, r, mid + 1, rb);
    69 }
    70 
    71 int main() {
    72     scanf("%d%d", &N, &Q);
    73     for (int i = 1; i <= Q; ++i) {
    74         scanf("%d%d%d%lld", opt + i, L + i, R + i, V + i);
    75         P[i] = i;
    76     }
    77     Solve(1, Q, -N, N);
    78     for (int i = 1; i <= Q; ++i) {
    79         if (opt[i] == 1) continue;
    80         printf("%d
    ", Ans[i]);
    81     }
    82     return 0;
    83 }
    View Code
  • 相关阅读:
    C#线程优先级浅析
    Android常用组件
    Android 内存监测工具 DDMS --> Heap
    Android 十个非常漂亮的常用组件
    RelativeLayout 相对布局 常用属性
    Android 关于横竖屏
    (转)Android 之 StrictMode 介绍
    Android如何获取SIM卡信息
    Android 读SIM卡信息
    Android Camera 使用小结
  • 原文地址:https://www.cnblogs.com/PinkRabbit/p/8039083.html
Copyright © 2011-2022 走看看