洛谷 4343 loj 2036 bzoj 4590 [SHOI2015]自动刷题机 题解

博客观赏效果更佳

题意简述

有一个刷题机,记录了这样的信息:有一个长度为 n < = 1 e 5 n<=1e5 的序列 a a ,表示写(>0)或删(<0)了若干行代码。如果删除的代码行数超过了已有的代码行数,那就是保持为 0 0 行代码。每当你的代码行数 > m >m 之后,你就会自动AC一个题,代码清空。现在已知你AC了 k k 个题。求 m m 的范围。无解输出-1.

思路

很明显, m m 越大 A C AC 的越少。二分即珂。(话说上海的题怎么这么水)

实现注意

我们怎么判断mid是取 ( l + r + 1 ) / 2 (l+r+1)/2 还是 ( l + r ) / 2 (l+r)/2 呢?

我刚开始学二分的时候也为这个事情发愁。但是后来我发现一个简单易懂的理解方法。

因为我经常会调试。调试发现,就是 r = l + 1 r=l+1 的时候陷入了一个死循环。

我们想想, r = l + 1 r=l+1 的时候, ( l + r ) / 2 (l+r)/2 相当于 l l ( l + r + 1 ) / 2 (l+r+1)/2 相当于 r r 。(换句话说,就是偏左的 m i d mid 和偏右的 m i d mid )。然后如果我们的条件是 l = m i d l=mid m i d mid 还取的是 ( l + r ) / 2 = l (l+r)/2=l ,那就相当于没有减小,会死循环。所以当条件是 l = m i d l=mid ,也就是要取最大值的时候, m i d mid 应该取 ( l + r + 1 ) / 2 (l+r+1)/2

同理,当我们要取最小值的时候,也就是 r = m i d r=mid 的时候,应该 m i d = ( l + r ) / 2 mid=(l+r)/2 ,这样才能让区间缩小。

#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
    #define int long long 
    #define N 155555
    #define F(i,l,r) for(int i=l;i<=r;++i)
    #define D(i,r,l) for(int i=r;i>=l;--i)
    #define Fs(i,l,r,c) for(int i=l;i<=r;c)
    #define Ds(i,r,l,c) for(int i=r;i>=l;c)
    #define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
    #define MEM(x,a) memset(x,a,sizeof(x))
    #define FK(x) MEM(x,0)

    void R1(int &x)
    {
        x=0;char c=getchar();int f=1;
        while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
        while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        x=(f==1)?x:-x;
    }
    int n,k;
    int a[N];
    void Input()
    {
        R1(n);R1(k);
        F(i,1,n) R1(a[i]);
    }

    int cnt(int m)//取m时的AC量
    {
        int S=0,C=0;
        F(i,1,n)
        {
            S+=a[i];
            if (S<0) S=0;
            if (S>=m) {C++;S=0;}
        }
        return C;
    }
    void Soviet()
    {
        int l=1,r=1e16;
        while(l<r)
        {
            int mid=(l+r)>>1;
            int C=cnt(mid);
            if (C<k) r=mid-1;//大了
            else if (C==k) r=mid;//正好,但是我们要取最小的,因为这边是求最小值
            else if (C>k) l=mid+1;//小了
        }
        if (cnt(l)!=k) {puts("-1");return;} //判一下无解
        printf("%lld ",l);//这个时候l==r,所以输出哪个都无妨
        //此时l=r=m的最小值

        l=1,r=1e16;
        while(l<r)
        {
            int mid=(l+r+1)>>1;
            int C=cnt(mid);
            if (C<k) r=mid-1; //大了
            else if (C==k) l=mid; //正好,但是我们要取最大的,因为这边是求最大值
            else if (C>k) l=mid+1; //小了 
        }
        printf("%lld\n",l); //同上
        //此时l=r=m的最大值
    }

    #define Flan void
    Flan IsMyWife()
    {
        Input();
        Soviet();
    }
    #undef int //long long 
}
int main()
{
    Flandre_Scarlet::IsMyWife();
    getchar();getchar();
    return 0;
}
发布了210 篇原创文章 · 获赞 8 · 访问量 8980

猜你喜欢

转载自blog.csdn.net/LightningUZ/article/details/103446573
今日推荐