分治法专辑

蓝桥学苑系列第二课,二分。查漏补缺吧。

  • lower_bound 查询(a,a+n)中>=x的第一个数的下标(从0开始)

  • upper_bound 查询(a,a+n)中>x的第一个数的下标(从0开始)


题目1链接:http://hihocoder.com/problemset/problem/1357?sid=1347312

题解:二分枚举时间t,并且对每个t用函数来判断。

           注意点在于题目说每秒回复伤害,其实就是给防护罩加血,加到满血为止。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
typedef long long ll;

const int inf = 0x3f3f3f3f;
const int maxn = 100005;
#define eps 1e-8

inline void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

inline void print(int x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9) print(x/10);
    putchar(x%10+'0');
}

ll n,m,k,a[maxn],b[maxn];

bool crash(int t)
{
    int cnt=m,tot=0;
    for(int i=0;i<n;i++)
    {
        if(a[i]>=cnt)
        {
            tot++;
            cnt = m;
        }
        else {
            cnt = min(m,cnt-a[i]+t);
        }
    }
    if(tot>=k)
        return false;
    return true;
}

int main()
{
    int cnt = 0;
    cin>>n>>m>>k;
    for(int i=0;i<n;i++)
    {
        cin>>a[i];
        if(a[i]>=m)
            cnt++;
    }
    if(cnt>=k) //如果能一炮打穿的炮弹数大于保护层数,直接输出-1
    {
        cout<<-1<<endl;
        return 0;
    }
    int l = 1,r = m;
    int ans;
    while(l<=r)
    {
        int mid = (l+r)>>1;
        if(crash(mid)) //只有在满足条件的情况下记录ans,最后一个被记录的ans一定为最优解
        {
            r = mid-1; //如果当前解已经是最优解,那么r会缩到比当前解小,但是缩了以后必然不满足条件
            ans = mid;
        }
        else
            l = mid+1;
    }
    cout<<ans<<endl;
    return 0;
}

题目2链接:http://hihocoder.com/problemset/problem/1692?sid=1347393

题意:略

题解:

       此题有毒。

       我们已知一个质数为a,一个(0,1)的数为x,那么质数a中所有小于等于x的子分数的个数恰好为x*a

       这一点并不是很难证明,假想质数a为5,x为3/5,那么小于等于其的分数自然而然有3个(1/5,2/5,3/5)。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
typedef long long ll;

const int inf = 0x3f3f3f3f;
const int maxn = 200005;
#define eps 1e-8

inline void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

inline void print(int x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9) print(x/10);
    putchar(x%10+'0');
}

ll n,k,p[maxn];

int main()
{
    cin>>n>>k;
    for(int i=0;i<n;i++)
        cin>>p[i];
    double l = 0.0, r = 1.0;
    while(true)
    {
        ll cnt=0,best_fz = -1,best_fm=-1;
        double mid = (l+r)/2;
        for(int i=0;i<n;i++)
        {
            ll x = p[i]*mid;
            cnt += x;
            if(best_fz==-1) { //如果没有存储最优值,就赋当前值
                best_fz = x;
                best_fm = p[i];
            }
            if(best_fz*p[i]<best_fm*x) { // x/p[i]是p[i]中<=x的最大的一个数,对于i∈[0,n),我们要取的是之前这些中的最大的,否则就不满足条件(正好为第k小)
                best_fz = x;
                best_fm = p[i];
            }
        }
        if(cnt == k) {
            cout<<best_fz<<"/"<<best_fm<<endl;
            return 0;
        }
        else if(cnt<k) l = mid;
        else r = mid;
    }
    return 0;
}

题目3链接:http://codeforces.com/contest/1011/problem/C

题意:就是有一艘飞船飞来飞去的,问满足条件最少要带多少油。

题解:这题学弟是递推做出来的,当然也可以用二分来做。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <map>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;

#define INIT(x) memset(x,0,sizeof(x))
typedef long long ll;

const int inf = 0x3f3f3f3f;
const int maxn = 200005;
#define eps 1e-8

inline void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

inline void print(int x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9) print(x/10);
    putchar(x%10+'0');
}

int n,m;
double a[maxn],b[maxn],mid;

int main()
{
    read(n),read(m);
    for(int i=0;i<n;i++)
        scanf("%lf",&a[i]);
    for(int i=0;i<n;i++)
        scanf("%lf",&b[i]);
    double l=1,r=1e9+10;
    int cnt = 0;
    while(true)
    {
        cnt++;
        if(cnt>=500)
            break;
        mid = (l+r)/2;
        double w = m+mid;
        w -= w/a[0];
        for(int i=1;i<n;i++)
        {
            w -= w/a[i];
            w -= w/b[i];
        }
        w -= w/b[0];
        if(abs(w-m)<eps){
            printf("%.10lf",mid);
            return 0;
        }
        else if(w<m) l = mid;
        else r = mid;
    }
    cout<<-1<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zxwsbg/article/details/81302825