luogu 4343 引用zzj大神的文章

原文章https://www.luogu.org/blog/zzj/ti-xie-p4343-shoi2015-zi-dong-shua-ti-ji-post

题目传送门

100


100 分(满分)思路:

我会暴力!

  • 枚举 nn 可能的最小值和最大值?显然会超时,时间复杂度约为 O((\sum_{i=1}^{n}x_i) \times n)O((i=1nxi)×n) 。显然会超时。

我会二分!

  • 我们考虑使用二分来解决这道题。时间复杂度约为 O(n\;log_2\;n)O(nlog2n) 。是可以在规定的时间内完成本题的。

那么怎么二分呢?

我们可以用两次二分来完成这道题,一次求最小值,一次求最大值。下面我拿求最小值的那次二分做举例,我们设 midmid 为可能的最小值且 mid=(l+r) \div 2mid=(l+r)÷2 ,在这里我用了一个 susu 以及一个 tt 来分别表示当前写的代码的行数以及现在已经A掉了多少题目,若 t \leq mtm (我在这里题目中的 kk )则让 r'=mid-1r=mid1 ,且如果 t=mt=m ,就让 ans=midans=mid (即 nn 可能的最小值)否则让 l'=mid+1l=mid+1 。直到 l>rl>r 时就跳出循环。下面上二分部分的代码:

long long l=1,r=inf;
while(l<=r)
{
    long long mid=(l+r)/2;
    long long su=0,t=0;
    for(long long i=1;i<=n;i++)
    {
        su+=a[i];
        if(su>=mid)
        {
            t++;
            su=0;
        }
        if(su<0)
        {
            su=0;
        }
    }
    if(t<=m)
    {
        r=mid-1;
        if(t==m)
        {
            ans=mid;
        }
    }
    else
    {
        l=mid+1;
    }
}
if(ans!=-1)
{
    printf("%lld ",ans);
    ans=-1,l=1,r=inf;
    while(l<=r)
    {
        long long mid=(l+r)/2;
        long long su=0,t=0;
        for(long long i=1;i<=n;i++)
        {
            su+=a[i];
            if(su>=mid)
            {
                t++;
                su=0;
            }
            if(su<0)
            {
                su=0;
            }
        }
        if(t<=m-1)
        {
            r=mid-1;

        }
        else
        {
            l=mid+1;
            if(t==m)
            {
                ans=mid;
            }
        }
    }
    printf("%lld",ans);
}
else
{
    printf("-1");
}
  • 一些坑点以及注意事项
  1. 二分的边界要设好,记住while(l<=r)不要写成while(l+1<=r)或者是while(l<=r-1)哦。

  2. rr 要设得足够大(例如999999999999999)。

  3. 记得要开 long\;longlonglong 哦。

下面上AC代码~

#include <cstdio>
long long a[1000001];
int main()
{
    long long inf=999999999999999,ans=-1,n=0,m=0;
    scanf("%lld %lld",&n,&m);
    for(long long i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
    }
    long long l=1,r=inf;
    while(l<=r)
    {
        long long mid=(l+r)/2;
        long long su=0,t=0;
        for(long long i=1;i<=n;i++)
        {
            su+=a[i];
            if(su>=mid)
            {
                t++;
                su=0;
            }
            if(su<0)
            {
                su=0;
            }
        }
        if(t<=m)
        {
            r=mid-1;
            if(t==m)
            {
                ans=mid;
            }
        }
        else
        {
            l=mid+1;
        }
    }
    if(ans!=-1)
    {
        printf("%lld ",ans);
        ans=-1,l=1,r=inf;
        while(l<=r)
        {
            long long mid=(l+r)/2;
            long long su=0,t=0;
            for(long long i=1;i<=n;i++)
            {
                su+=a[i];
                if(su>=mid)
                {
                    t++;
                    su=0;
                }
                if(su<0)
                {
                    su=0;
                }
            }
            if(t<=m-1)
            {
                r=mid-1;

            }
            else
            {
                l=mid+1;
                if(t==m)
                {
                    ans=mid;
                }
            }
        }
        printf("%lld",ans);
    }
    else
    {
        printf("-1");
    }
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值