[USACO14MAR] 破坏Sabotage(二分答案,分数规划)

题目链接

Solution

去掉中间一段区间 \([l,r]\) 后剩下的平均值可以表示为 :

\[\frac{\sum^{n}_{i=1}{v_i}-\sum^{r}_{i=l}{v_i}}{n-(r-l+1)} \]

二分的答案如果要满足条件,即:

\[\frac{\sum^{n}_{i=1}{v_i}-\sum^{r}_{i=l}{v_i}}{n-(r-l+1)}<Ans \]

移一下项,然后可以得到:

\[{\sum^{n}_{i=1}{(v_i-Ans)}<\sum^{r}_{i=l}{(v_i-Ans)}} \]

然后这两个式子的意义就是全局 \(v_i-Ans\) 的和比中间这段 \(v_i-Ans\) 要小.
所以只需要有一段前缀和与一段后缀和之和小于等于 \(0\) ,那么答案即满足条件。

Code

#include<bits/stdc++.h>
#define db double
#define ll long long
#define inf 0x3f3f3f3f3f3f3f
#define N 100008
using namespace std;
const db opt=0.00001;

void in(ll &x)
{
    char ch=getchar();ll f=1,w=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
    x=f*w; return;
}

db a[N];ll n;

bool judge(db st)
{
    db sum[N],dum[N],sm[N],dm[N];
    sum[0]=dum[N+1]=0; 
      sm[0]=dm[n+1]=inf;
    for(int i=1;i<=n;i++)
       sum[i]=sum[i-1]+a[i]-st,
          sm[i]=min(sm[i-1],sum[i]);
    for(int i=n;i>=1;i--)
       dum[i]=dum[i+1]+a[i]-st,
          dm[i]=min(dm[i+1],dum[i]);
    for(int i=2;i<n;i++)
       if(sm[i-1]+dm[i+1]<=0)return 1;
    return 0;       
}

int main()
{
    db ans=inf,l=inf,r=0;
    in(n);
    for(int i=1;i<=n;i++)
    {
        ll x; in(x);
        a[i]=x*1.0;
        l=min(a[i],l);
        r=max(a[i],r);
    }	
    while(l<=r)
    {
        db mid=(l+r)/2;
        if(judge(mid))ans=min(ans,mid),r=mid-opt;
        else l=mid+opt;
    }
    if(ans==inf)printf("%.3lf\n",(a[1]+a[n])/(2*1.0));
    else printf("%.3lf\n",ans);
    return 0;
}
posted @ 2019-07-18 20:20  Kevin_naticl  阅读(221)  评论(0编辑  收藏  举报