UVALive - 7501、Business Cycle (二分、思维)

题目vj链接

题面:
在这里插入图片描述

题意:
给定一个有 n n n 个节点的环,每个节点有一个权值 v i v_i vi,初始时我有一个权值 v a l val val,且我在 0 0 0 号节点以外(走一步到达 0 0 0 号节点)。 n − 1 n-1 n1 号节点的下一步是 0 0 0 号节点。
我每到达一个节点, v a l = m a x ( v a l + v i , 0 ) val=max(val+v_i,0) val=max(val+vi,0),即我每到一个节点都加上这个节点的权值,如果当前我的权值为负数,那么就变为 0 0 0

问,我的初始权值至少为多少,才能保证我在 p p p 步之内的权值能到达 g g g
其中 p p p 步之内包括 0 0 0 步和 p p p 步。

题解:
很显然,我们二分答案,然后检验答案是否符合。
但是这里 p p p 十分的大,我们不可能去 O ( p ) O(p) O(p) c h e c k check check
但是注意到,我要走的路径一定是在一个环上的,考虑 p > 2 ∗ n p>2*n p>2n 的一般情况。 p ≤ 2 ∗ n p\le 2*n p2n 直接 O ( p ) O(p) O(p) check即可。

p > 2 ∗ n p>2*n p>2n 时。
假设当前二分的初始权值为 m i d mid mid
我们暴力在环上走一圈,记走一圈之后的权值为 a n s 1 ans1 ans1,在整个过程中产生的最大值为 m a x x maxx maxx
如果当前最大值 m a x x > = g maxx>=g maxx>=g 则可以。
如果当前最大值 m a x x < g maxx<g maxx<g,且 a n s 1 ≤ m i d ans1\le mid ans1mid 则表明以后的最大值不可能超过 m a x x maxx maxx 则不可以。

其他情况 : m a x x < g   a n d   a n s 1 > m i d maxx<g\space and\space ans1>mid maxx<g and ans1>mid,我们暴力再在环上走一圈,记再走一圈之后的权值为 a n s 2 ans2 ans2。整个过程中的最大值为 m a x x maxx maxx
如果当前最大值 m a x x > = g maxx>=g maxx>=g 则可以。
如果当前最大值 m a x x < g maxx<g maxx<g,且 a n s 1 = a n s 2 ans1=ans2 ans1=ans2 则表明以后依然是重复当前圈的权值,则不可以。

其他情况 : m a x x < g   a n d   a n s 2 > a n s 1 maxx<g\space and\space ans2>ans1 maxx<g and ans2>ans1 ,这种情况表明在初始值为 a n s 1 ans1 ans1 时,循环过程中已经没有地方的权值会让我的权值变为 0 0 0 了。
考虑反证,假设初始值为 a n s 1 ans1 ans1 时,循环过程中有地方能让我的权值变为0,且 a n s 1 > m i d ans1>mid ans1>mid ,那么在初始权值为 m i d mid mid 的时候肯定也在 a n s 1 ans1 ans1 情况下权值变为0的地方让我的权值变为 0 0 0。那么这样最终应该是 a n s 1 = a n s 2 ans1=ans2 ans1=ans2

所以现在的差值 a n s 2 − a n s 1 ans2-ans1 ans2ans1 是转一圈之后每一个位置会净增加的量。

计算 a d d = ( p − 2 ∗ n ) / n ∗ ( a n s 2 − a n s 1 ) add=(p-2*n)/n*(ans2-ans1) add=(p2n)/n(ans2ans1) a d d add add 为每个位置至少还要增加的量。
其中 a d d add add 有可能爆掉 l o n g   l o n g long\space long long long

那么我们取 m a x x = m a x ( m a x x , m a x x + a d d ) , a n s 2 = a n s 2 + a d d maxx=max(maxx,maxx+add),ans2=ans2+add maxx=max(maxx,maxx+add)ans2=ans2+add

此时,我们还剩下 p   m o d   n p\space mod\space n p mod n 步,没有走。此时 a n s 2 ans2 ans2 就是最后 p   m o d   n p\space mod\space n p mod n 步的初始值,暴力走完即可。

时间复杂度 O ( T ∗ n l o g n ) O(T*nlogn) O(Tnlogn)

做题的时候,二分check的返回条件写错了,应该返回 m a x x > = g maxx>=g maxx>=g,结果写成的 m a x x > = m i d maxx>=mid maxx>=mid,调了个寂寞。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<unordered_set>
#include<set>
#include<ctime>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
#define lc (cnt<<1)
#define rc (cnt<<1|1)
#define len(x)  (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
#define fhead(x) for(int i=head[(x)];i;i=nt[i])
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;

const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const double alpha=0.75;
const int mod=998244353;
const double eps=1e-8;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=100100;
const int maxm=100100;
const int maxp=100100;
const int up=1100;

ll a[maxn];
ll n,g,p;

bool check(ll mid)
{
    if(mid>=g) return true;//初始就符合
	
	//开始跑第一圈 
    ll ans1=mid;
    ll maxx=mid;
    for(int i=0;i<n&&i<p;i++)
    {
        ans1=ans1+a[i];
        if(ans1<0) ans1=0;
        maxx=max(maxx,ans1);
    }
    if(p<=n) return maxx>=g;//已经跑完p步 
    if(maxx>=g) return true;//已经符合要求 
    if(ans1<=mid) return false;//之后的maxx一定不优于当前的
	 
    ll ans2=ans1;
    for(int i=0;i<n&&i+n<p;i++)
    {
        ans2=ans2+a[i];
        if(ans2<0) ans2=0;
        maxx=max(maxx,ans2);
    }
    if(p<=2*n) return maxx>=g;//已经跑完p步 
    if(maxx>=g) return true;//已经符合要求 
    if(ans2==ans1) return false;//在初始值ans1,进入循环,答案不会更优
	
	 
    __int128 add=(p-2*n)/n*(__int128)(ans2-ans1);
    if(maxx+add>=g) return true;//当前最大值加上往后每轮增加的值符合要求 
    
    ll x=p%n;
	ans2+=add;//最后x步起始值 
    for(int i=0;i<n&&i<x;i++)
    {
        ans2=ans2+a[i];
        if(ans2<0) ans2=0;
        maxx=max(maxx,ans2);
    }
    return maxx>=g;
}

int main(void)
{
    int tt;
    scanf("%d",&tt);
    int pp=0;
    while(tt--)
    {
        scanf("%lld%lld%lld",&n,&g,&p);
        for(int i=0;i<n;i++)
            scanf("%lld",&a[i]);
            
        ll l=0,r=g;
        ll ans=0,mid;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(check(mid)) ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("Case #%d: %lld\n",++pp,ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值