zoukankan      html  css  js  c++  java
  • 二分搜索

    最近好像高频遇见三类题:

    利用C++ STL会炒鸡机智+优雅的模拟...

    尺取法...

    二分搜索...

    二分搜索是O(log(n))的,然后,要求询问内容在区间内单调。

    UVALive 7292 Refract Facts

    如题,求角度。

    关于double的二分,如果不加break条件会死循环,然后,转移条件:R = mid or L = mid.

    由公式的两个角度,对其中一个二分即可。

    注意精度问题,1e-4 WA sample3. 1e-5 AC. 常规来讲,大概可以是1e-10 或者 .. 1e-99 ? 2333

    #include <stdio.h>
    #include <iostream>
    #include <string.h>
    #include <math.h>
    #include <cmath>
    #define pie acos(-1)
    #define eps 1e-5
    using namespace std;
    
    int main() {
        //freopen("in.cpp", "r", stdin);
        double d, h, x, n1, n2;
        while(~scanf("%lf%lf%lf%lf%lf", &d, &h, &x, &n1, &n2)) {
            if (d == 0 && h == 0 && x == 0 && n1 == 0 && n2 == 0) break;
            double l=0, r = pie/2;
            double mid;
            while(r-l>eps) {
                mid = (l+r)*1.0/2; // degree1
                double deg2 = asin(n2*1.0/n1*sin(mid));
                if (abs(d*tan(mid) + h*tan(deg2) - x) < 1e-5)
                    break;
                if (d*tan(mid) + h*tan(deg2) - x > eps) {
                    r = mid;
                }
                else {
                    l = mid;
                }
            }
            printf("%.2f
    ", 90.0 - mid*180/pie);
        }
        return 0;
    }
    

      

    CF 689D Friends and Subsequences

    题意:给两个数组,问有多少个区间满足该区间内a[]的最大值和b[]的最小值。

    关于区间询问的常见算法:

    线段树,莫队,RMQ,询问都是O(1)的,如果是依次询问,会有n*(n+1)/2个,n <=2*10^5,毫无疑问TLE。挣扎了一下,挂在sample 7.

    所以应该想到,肯定不会是n*(n+1)/2个询问。可以发现,对任意的一个区间,固定左端点,maxa - minb 的值是单调递增的。单调递增?也就是说,对于任意

    的一个左端点,与之对应的右端点一定是一个连续的区间或者不存在。那么,我们就可以通过二分查找来确定右端点的边界。

    确定左边界时:寻找第一个maxa ==minb 的值,所以当前==的时候,也要左移。 if (maxa <= minb) 左移;else 右移;

    同理,确定右边界时:寻找最后一个maxa == minb的值,所以==的时候,右移。if (maxa < minb) 左移;else 右移;

    时间复杂度:RMQ预处理O(n*log(N)) ,二分查找:O(log(N))。

    #include <stdio.h>
    #include <iostream>
    #include <string.h>
    #define maxn 200100
    #define LL long long
    using namespace std;
    
    int a[maxn], b[maxn];
    int dpa[maxn][20], dpb[maxn][20];
    int mm[maxn];
    
    int max_(int a, int b) {
        return a > b ? a : b;
    }
    
    int min_(int a, int b) {
        return a < b ? a : b;
    }
    
    void initRMQa(int n, int a[]) {
        mm[0] = -1;
        for (int i=1; i<=n; ++i) {
            mm[i] = ((i&(i-1)) == 0) ? mm[i-1]+1 :mm[i-1];
            dpa[i][0] = a[i];
        }
        for (int j=1; j<=mm[n]; ++j) {
            for (int i=1; i+(1<<j)-1<=n; ++i) {
                int ta = dpa[i][j-1];
                int tb = dpa[i+(1<<(j-1))][j-1];
                dpa[i][j] = max_(ta, tb);
            }
        }
    }
    
    void initRMQb(int n, int a[]) {
        mm[0] = -1;
        for (int i=1; i<=n; ++i) {
            mm[i] = ((i&(i-1)) == 0) ? mm[i-1]+1 :mm[i-1];
            dpb[i][0] = b[i];
        }
        for (int j=1; j<=mm[n]; ++j) {
            for (int i=1; i+(1<<j)-1<=n; ++i) {
                int ta = dpb[i][j-1];
                int tb = dpb[i+(1<<(j-1))][j-1];
                dpb[i][j] = min_(ta, tb);
            }
        }
    }
    
    int queryA(int x, int y) {
        int k = mm[y-x+1];
        return max_(dpa[x][k], dpa[y-(1<<k)+1][k]);
    }
    
    int queryB(int x, int y) {
        int k = mm[y-x+1];
        return min_(dpb[x][k], dpb[y-(1<<k)+1][k]);
    }
    
    int main(){
       // freopen("in.cpp", "r", stdin);
        int n;
        while(~scanf("%d", &n)) {
            for (int i=1; i<=n; ++i) scanf("%d", &a[i]);
            for (int i=1; i<=n; ++i) scanf("%d", &b[i]);
            initRMQa(n, a);
            initRMQb(n, b);
            LL ans = 0;
            // solve
            for (int i=1; i<=n; ++i) {
                int ansl = -1, ansr = -1;
                int l = i, r = n;
                while(l<=r) { // l
                    int mid = (l+r)/2;
                    int maxa = queryA(i, mid);
                    int minb = queryB(i, mid);
                    if (maxa >= minb) {
                        r = mid - 1;
                        if (maxa == minb) ansl = mid;
                    }
                    else l = mid + 1;
                }
                l = i, r = n;
                int cnt = 0;
                while(l<=r) {
                    while(l<=r) { //r
                    int mid = (l+r)/2;
                    int maxa = queryA(i, mid);
                    int minb = queryB(i, mid);
                    if (maxa > minb) {
                        r = mid - 1;
                    }
                    else {
                        l = mid + 1;
                        if (maxa == minb) ansr = mid;
                    }
                   }
                }
                if (ansl != -1 && ansr != -1 && ansl <= ansr) {
                    ans += (ansr - ansl + 1);
                }
            }
            printf("%I64d
    ", ans);
        }
        return 0;
    }
    

    CF 689C  Mike and Chocolate Thieves1

    题意:给出一个数m,问最小的n满足,所有小于等于n的数组成的k倍序列(a  a*k  a*k^2  a*k^3)刚好是10个。

    可以发现每个元组由a唯一确定,那么,对于数n,若m=n / (k*K*k) ,则说明a 的取值可以是 [1, m]的其中一个,即可以确定这样的序列有m个。

    于是...对 1和 1e18区间内的数,二分查找符合题意的n的最小值...就可以了..

    也就是说..此时我们已经知道了如何判断当前数是否符合条件,并且能够在不断的二分区间的情况下,找到正确的答案。时间O(log(N))。

    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #define LL long long
    using namespace std;
    
    LL check(LL n) {
        LL i;
        LL ans = 0;
        for (i=2; i*i*i<=n; ++i) {
            ans += n / (i*i*i);
        }
        return ans;
    }
    
    int main() {
       // freopen("in.cpp", "r", stdin);
        LL n;
        while (~scanf("%I64dd", &n)) {
            LL l = 1, r = 1e18;
            LL ans = 0;
            while(l <= r) {
                LL mid = (l + r) / 2;
                LL temp = check(mid);
                if (temp >= n) {
                    r = mid - 1;
                    if (temp == n) ans = mid;
                }
                else l = mid + 1;
            }
            if (ans == 0) printf("-1
    ");
            else printf("%I64d
    ", ans);
        }
        return 0;
    }//
    

    即发现了..树状数组很优雅...动态规划很优雅...之后...成功发现了二分也很优雅的事实....23333

  • 相关阅读:
    Codeforces Round #433 (Div. 2, based on Olympiad of Metropolises)
    树的遍历(前序中序求后序,后序中序求前序)
    Codeforces Round #439 (Div. 2)
    python3 调用百度api实现语音识别
    Win10 opencv cuda + 扩展库 vs2019 cuda10
    flask读取摄像头并实时显示
    树莓派设置开机启动
    树莓派python OLED使用
    UART Fingerprint Sensor (C)树莓派使用
    树莓派全版本换源(多环境测试无误版)
  • 原文地址:https://www.cnblogs.com/icode-girl/p/5755202.html
Copyright © 2011-2022 走看看