luoguP2115 [USACO14MAR]破坏Sabotage

Date: Wed 07 November 2018
Updated: Wed 07 November 2018

In 5. OI.

Tags: OI

题目


不能直接跑最大连续子段和,因为平均数不确定,如果该点产量在平均数以下则为正贡献,反之为负贡献,无法确定平均数,因此无法确定舍去哪些点。

考虑二分答案。

\(a[i]<=x\)则为正贡献,若\(a[i]>x\)则为负贡献,因此每次check先把\(a[i]\)减去x。跑最大连续子段和。然后验证除去最大连续子段后的sum是否小于等于0,如果是则mid合法。


code
#include<bits/stdc++.h>
#define N 100010
#define eps (0.00001)
#define max(a,b) ((a)>(b)?(a):(b))
#define mem(a,b) memset(a,b,sizeof(a))
#define fsb(a,b,c) for(int a=b;a<=(c);a++)
#define fbs(a,b,c) for(int a=b;a>=(c);a--)
using namespace std;
double a[N],f[N],g[N],sum=0,l,r,md;
int n;
template<typename qw>inline void rll(qw &x){
 x=0;qw f=1;char c=getchar();
 while(!isdigit(c))f=c=='-'?-1:f,c=getchar();
 while(isdigit(c))x=x*10+c-'0',c=getchar();
 x*=f;
}
inline int check(double x){
 mem(f,0);mem(g,0);int mx=0;double sum=0; 
 fsb(i,2,n-1){
  if(f[i-1]>0){
   g[i]=g[i-1];f[i]=f[i-1]+a[i]-x;
  }else{
   f[i]=a[i]-x;g[i]=i;
  }
  if(mx==0||f[mx]<f[i])mx=i;
 }
 fsb(i,1,g[mx]-1)sum+=a[i]-x;
 fsb(i,mx+1,n)sum+=a[i]-x;
 return sum<=0;
}
int main(){
 rll(n);fsb(i,1,n)rll(a[i]),sum+=a[i];
// fsb(i,1,n)printf("%lf ",a[i]);
 l=0;r=sum;
 while(l+eps<r){
  md=(l+r)/2;
  if(check(md))r=md;else l=md;
 }
 printf("%0.3lf\n",md);
 return 0;
}

注意精度

Social