zoukankan      html  css  js  c++  java
  • UVALive 7501 Business Cycle(二分)题解

    题意:n个数,有一个起始值,按顺序从第一个开始不断循环取数,如果取完后相加小于0就变为0,最多取p个数,问你得到大于等于值g所需要的最小起始值为多少

    思路:这题目爆long long爆的毫无准备,到处都有可能爆值。

    显然,我们能想出,初始值越大,那么走相同步数所得到的数字就会越大(或相等),那么我们就可以用二分法每次判断是否能得到g值,大概logG * n * C的复杂度。那么现在问题就是怎么判定初始值s是否能得到g值。

    我们可以求循环两次的结果差dis = tot2 - tot1,来判断每次循环的走向是增还是减或者不变。之所以算两次循环是因为可能遇到这样的样例:

    4 5 10

    1 2 -100 20

    假设我初始值为10,那么第一次循环后得到20,那么其实不是一次循环就会增长10,因为第二次循环后还是20,所以要循环两次判断走势。

    p <= 2n 或者 dis <= 0直接遍历两次循环就行了; p > 2n时,我们就让他一直循环,然后遍历最后一圈+ mod(就是p % n)。为什么不是只遍历最后的mod,而是要提前一圈遍历?因为有可能我在最后一圈里可能遇到在最后几个数中出现了-INF的超小值,我吃了这个超小值之后就不划算了。

    最后注意别随时有可能爆long long...orz

    代码:

    #include<set>
    #include<map>
    #include<cmath>
    #include<queue>
    #include<string>
    #include<cstdio>
    #include<cstring>
    #include<sstream>
    #include<algorithm>
    typedef long long ll;
    using namespace std;
    const int maxn = 1e5 + 10;
    const int MOD = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    ll v[maxn];
    ll n, g, p;
    bool getMax(ll st){
        if(st >= g) return true;
        if(p <= n){
            ll tot = st;
            for(ll i = 0; i < p; i++){
                tot += v[i];
                if(tot < 0) tot = 0;
                if(tot >= g) return true;
            }
            return false;
        }
        else{   //p > n
            ll tot = st;
            for(ll i = 0; i < n; i++){
                tot += v[i];
                if(tot < 0) tot = 0;
                if(tot >= g) return true;
            }
            ll last = tot;
            if(p <= 2 * n){
                for(ll i = 0; i < p - n; i++){
                    tot += v[i];
                    if(tot < 0) tot = 0;
                    if(tot >= g) return true;
                }
                return false;
            }
            else{   //p > 2n
                for(ll i = 0; i < n; i++){
                    tot += v[i];
                    if(tot < 0) tot = 0;
                    if(tot >= g) return true;
                }
                ll dis = tot - last;
                if(dis <= 0) return false;
                else{
                    ll k = p / n, rest;
                    k--;
                    if(k >= g / dis) return true;   //防止爆long long
                    rest = p - k * n;
                    tot = last + dis * (k - 1LL);
                    if(tot >= g) return true;
                    for(ll i = 0; i < n; i++){
                        tot += v[i];
                        if(tot < 0) tot = 0;
                        if(tot >= g) return true;
                    }
                    rest -= n;
                    for(ll i = 0; i < rest; i++){
                        tot += v[i];
                        if(tot < 0) tot = 0;
                        if(tot >= g) return true;
                    }
                    return false;
                }
            }
        }
    }
    int main(){
        int t, ca = 1;
        scanf("%d", &t);
        while(t--){
            scanf("%lld%lld%lld", &n, &g, &p);
            for(ll i = 0; i < n; i++){
                scanf("%lld", &v[i]);
            }
            ll l = 0, r = g, ans = g;
            while(l <= r){
                ll m = (l + r) / 2;
                if(getMax(m)){
                    r = m - 1;
                    ans = m;
                }
                else{
                    l = m + 1;
                }
            }
            printf("Case #%d: %lld
    ", ca++, ans);
        }
        return 0;
    }
  • 相关阅读:
    这鸡汤、真香
    linux 编译安装python并且安装虚拟环境工具
    前端数据删除
    前后端分离DRF项目初始化
    ubuntu 安装nginx docker
    ubuntu安装vue
    虚拟环境安装
    sql语句优化
    Python之网络编程 进程 线程 协程
    Python之网络编程 文件上传 基于udp的协议的socket socketsever同时接收多方消息
  • 原文地址:https://www.cnblogs.com/KirinSB/p/10332283.html
Copyright © 2011-2022 走看看