SHOI2015 自动刷题机

题目链接:戳我

我也想要一个!!!

显然能够看出来单调性——当N越大,能AC的题越少,反之越少。所以。。。很明显的二分答案对吧。

唯一一点不太一样的是,它要求可能的最大值,和可能的最小值。

那我们就写两个check,先求出来最大值,如果算出来的AC题数大于等于K,我们就选择右区间。

然后再0和最大值的区间中再次二分(减小带的log的大小),因为是求最小值,所以如果AC题数小于等于K,我们就选择左区间。

最后注意如果最小值算出来是0的话还是要+1的,因为题目中说要求非负。

什么时候不合法?当最小值大于最大值(为什么会这样?,大家可以手动模拟一下,根据我写的这种二分的原理,如果中间没有==k的,那么便会出现这种情况)或者最小值和最大值都是0的时候。。。就非法啦!

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define MAXN 100010
using namespace std;
int n,k;
int a[MAXN];
long long cur_ans;
inline bool check(long long x)
{
    long long sum=0,ans=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]<0&&sum+a[i]<0) sum=0;
        else sum+=a[i];
        if(sum>=x)
        {
            ans++,sum=0;
        }
    }
    if(ans>=k) return true;
    else return false;
}
inline bool check2(long long x)
{

    long long sum=0,ans=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]<0&&sum+a[i]<0) sum=0;
        else sum+=a[i];
        if(sum>=x) ans++,sum=0;
    }
    if(ans>k) return true;
    else return false;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    #endif
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    long long l=0,r=(long long)1e14;
    while(l<r)
    {
        long long mid=(l+r)>>1;
        if(check(mid)) l=mid+1;
        else r=mid;
    }
    cur_ans=l-1;
    r=l,l=0;
    while(l<r)
    {
        long long mid=(l+r)>>1;
        if(check2(mid)) l=mid+1;
        else r=mid;
    }
    if((l==0&&cur_ans==0)||(cur_ans<l)){printf("-1\n");return 0;}
    if(l==0) l++;
    printf("%lld %lld\n",l,cur_ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/fengxunling/p/10418453.html