2792: [Poi2012]Well 思路题 二分答案

我只知道要二分答案。。然后处理好差值。。然后哪个点为0我就不会判了QAQ。

于是膜了一发Claris:

枚举要变为0的位置,求出L,R使得:
a[L]>(i-L)mid
a[R]>(R-i)mid
此时只需要把[L,i]和[i,R]修改成一个等差数列即可满足条件且代价最小。
注意到随着i的右移,L递增;随着i的左移,R递减,所以可以O(n)完成判定。

POI的题目好神奇啊= =(我太弱啦)

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
int n,k,L,R;
ll m;
int a[1000005],b[1000005];
ll s[1000005],f[1000005];
inline ll read()
{
    ll a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline int judge(int mid)
{
    b[1]=a[1]; 
    ll now=0;
    for (int i=2;i<=n;i++) b[i]=min(a[i],b[i-1]+mid);
    for (int i=n-1;i;i--) b[i]=min(b[i],b[i+1]+mid);
    for (int i=1;i<=n;i++) now+=a[i]-b[i],s[i]=s[i-1]+(ll)b[i];
    if (now>m) return 0;
    int j=1;
    for (int i=1;i<=n;i++)
    {
        while (j<i&&b[j]<=(ll)(i-j)*mid) j++;
        f[i]=s[i-1]-s[j-1]-(ll)(i-j+1)*(i-j)*mid/2;
    }
    j=n;
    for (int i=n;i;i--)
    {
        while (j>i&&b[j]<=(ll)(j-i)*mid) j--;
        f[i]+=s[j]-s[i]-(ll)(j-i+1)*(j-i)*mid/2;
    }
    for (int i=1;i<=n;i++)
        if (f[i]+b[i]+now<=m) return i; 
    return 0;
}
int main()
{
    n=read(); m=read();
    for (int i=1;i<=n;i++) a[i]=read(),R=max(R,a[i]);
    while (L<=R)
    {
        int mid=L+R>>1,t;
        if (t=judge(mid)) R=mid-1,k=t; else L=mid+1;
    }
    printf("%d %d\n",k,L);
    return 0;
}

你可能感兴趣的:(2792: [Poi2012]Well 思路题 二分答案)