BZOJ 2792 Poi2012 Well 二分答案

题目大意:给定一个非负整数序列 A ,每次操作可以选择一个数然后减掉1,要求进行不超过 m 次操作使得存在一个 Ak=0 max{AiAi+1} 最小,输出这个最小值以及此时最小的 k
二分答案,然后验证的时候首先让相邻的都不超过 x ,然后枚举哪个点应该改成 0
如果某个点需要改成 0 ,那么需要进行操作的位置是一段区间,左右端点都单调,扫两遍就行了

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 1001001
using namespace std;
int n,pos,a[M];
long long m;
bool Judge(long long limit)
{
    long long cost=0;
    int i,j;

    static int b[M];

    for(i=1;i<=n;i++)
        b[i]=a[i];

    for(i=2;i<=n;i++)
        if(b[i]-b[i-1]>limit)
        {
            cost+=b[i]-b[i-1]-limit;
            b[i]=b[i-1]+limit;
        }

    for(i=n-1;i;i--)
        if(b[i]-b[i+1]>limit)
        {
            cost+=b[i]-b[i+1]-limit;
            b[i]=b[i+1]+limit;
        }

    if(cost>m) return false;

    static long long sum[M];

    for(i=1;i<=n;i++)
        sum[i]=sum[i-1]+b[i];

    static int l[M],r[M];

    for(j=1,i=1;i<=n;i++)
    {
        while( b[j]<(long long)(i-j)*limit )
            j++;
        l[i]=j;
    }

    for(j=n,i=n;i;i--)
    {
        while( b[j]<(long long)(j-i)*limit )
            j--;
        r[i]=j;
    }

    for(i=1;i<=n;i++)
    {
        long long _cost=(sum[r[i]]-sum[l[i]-1]);
        _cost-=(long long)limit*(i-l[i])*(i-l[i]+1)>>1;
        _cost-=(long long)limit*(r[i]-i)*(r[i]-i+1)>>1;
        if(cost+_cost<=m)
            return pos=i,true;
    }
    return false;
}
int Bisection()
{
    int l=0,r=1000000000;
    while(r-l>1)
    {
        int mid=l+r>>1;
        if( Judge(mid) )
            r=mid;
        else
            l=mid;
    }
    return Judge(l)?l:r;
}
int main()
{
    #ifdef PoPoQQQ
    freopen("stu_tests\\stu4c.in","r",stdin);
    #endif
    int i;
    cin>>n>>m;
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    int ans=Bisection();
    Judge(ans);
    cout<<pos<<' '<<ans<<endl;
    return 0;
}

你可能感兴趣的:(bzoj,二分答案,BZOJ2792)