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\n", ca++, ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/KirinSB/p/10332283.html

时间: 2024-11-09 00:39:45

UVALive 7501 Business Cycle(二分)题解的相关文章

UVALive - 3211 (2-SAT + 二分)

layout: post title: 训练指南 UVALive - 3211 (2-SAT + 二分) author: "luowentaoaa" catalog: true mathjax: true tags: - 2-SAT - 图论 - 训练指南 Now or later UVALive - 3211 题意 n架飞机,每架可选择两个着落时间.安排一个着陆时间表,使得着陆间隔的最小值最大 题解 二分查找最大值P,每次都用2-SAT判断是否可行. #include<bits

UVALive 6168 Fat Ninjas --二分小数+搜索

题意:一个NxN的网格地板,有一些激光束从天花板垂直射向地面的某个网格,一个圆要安全地从左走到右,不碰到上边界,下边界以及激光束,问这个圆的直径最大能达到多大. 分析:可以二分直径,关键在check函数的写法.可以讲这个圆缩成一个点,把圆的直径转化为激光的扫描范围,当激光范围完全堵死一条通道的时候,这个直径则是不可行的.怎样判断是否堵死一条通道了呢.每次check(dis)的时候,枚举激光束对,如果激光束之间距离小于dis,那么它们两个之间建一条边.还要注意处理边界,如果激光束范围与上边界或下边

uva11090 Going in Cycle!! --- 二分+spfa判负环

给一个带权有向图,求其中是否存在环,若存在,输出环上边权的平均值最小的那个的平均值. 点的范围就50,感觉可以很暴力..但显然超时了 感觉方法好巧妙,二分平均值,将所有边权减去二分的那个值,然后spfa判断是否有负环 若有负环,则图中存在的所有环的边权平均值一定比枚举值大 反之则小,要是无论枚举值多大都没有负环,说明图中没有环. #include <iostream> #include <cstring> #include <string> #include <c

UVAlive7501 Business Cycle 2015ECfinal B(二分模板)

题意:给你一个环,环有n个点,编号0~n-1,每个点有一定的权值,从点0出发沿编号走,到达某一个节点则把目前总权值加上这个节点的权值,如果结果小于0则变成0.现在给你最多可以走的步数P和最大需要到达的权值大小G,问你需要的最小的初始权值为多少,能在P步内能够产生的最大权值大于等于G 题解:很容易想到初始权值越大,经过同样步数能够得到的权值就越大,当然是非严格递增的.那么直接想办法二分寻找初始权值就可以.但是如果步数太大了就没法模拟了,所以要消除步数的影响.步数的影响主要来源于在走环的过程中可能由

UVALive 3971 Assemble(模拟 + 二分)

UVALive 3971 题意:有b块钱,想要组装一台电脑,给出n个配件的种类,名字,价格,品质因子.若各种类配件各买一个,总价格<=b,求最差品质配件的最大品质因子. 思路: 求最大的最小值一般用二分法. 在(0,maxq)内进行二分,判定q作为最差品质因子是否可行. 大白书原题,比较考验代码功底. code: /* * @author Novicer * language : C++/C */ #include<iostream> #include<sstream> #i

UVAlive 7366 Brocard(二分+计算几何)

题目地址:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5388 思路:二分Brocard angle设为rad,将两条边(AB,BC)按该角度旋转,求出两条边的交点P,判断CA与CP所成角度ang是否等于该角度,若rad>ang则rad应减小(r=mid),即使得ang变大,否则rad应增大(l=mid),直到两者相

UVALive 3971 Assemble(二分+贪心)

本题思路不难,但是要快速准确的AC有点儿考验代码功力. 看了大白书上的标程,大有所获. 用map和vector的结合给输入分组,这个数据结构的使用非常精美,恰到好处. #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<cmath> #include<map> #include&l

UVALive 6525 Attacking rooks 二分匹配 经典题

题目链接:option=com_onlinejudge&Itemid=8&page=show_problem&problem=4536">点击打开链接 题意: 给定n*n的棋盘, 能够在'.'上摆 象棋中的车(X是墙壁) 使得随意两个车都不能互相攻击到 问:最多能摆多少个车. 思路: 二分匹配 1.若没有X.那么做法就是 X点集为行,Y点集为列,对于图上的每一个点所在的行和列(x,y) 建一条边 x->y 2.有了X,那么对于每一个点所在的上方能接触到的X必须

uvalive 4254 Processor处理器 (二分模拟+贪心)

 有n个任务,每个任务有ri,di,wi;代表任务的[ri,di]代表可以做这个任务的时间区间,而wi代表这个任务的工作量;现在有有个处理器,如果它的执行速度是s,则完成第i个任务所需时间wi/s;要求算出处理器执行过程中最大速度的最小值 思路很简单二分,但如何模拟是难点,可以模拟处理器每一秒的工作,对于每一秒来说,用优先队列储存当前时间下可以处理的任务,优先处理d小的,如果处理完了,那么处理下一个任务,如果没处理完,时间加一. #include<cstdio> #include<c