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

利用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\n", 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\n", 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\n");
        else printf("%I64d\n", ans);
    }
    return 0;
}//

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

 

posted on 2016-08-10 00:50  小小八  阅读(233)  评论(0编辑  收藏  举报