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

来源:互联网 发布:每天算工资软件 编辑:程序博客网 时间:2024/05/17 23:04

我只知道要二分答案。。然后处理好差值。。然后哪个点为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;}
0 0