【CodeForces】578 C. Weakness and Poorness
【题意】给定含n个整数的序列ai,定义新序列为ai-x,要使新序列的最大子段和绝对值最小,求实数x。n<=2*10^5。
【算法】二分||三分||计算几何(凸包)
【题解】Editorial
令正数最大子段和为A,负数最大子段和为B,绝对值是max(A,B)。当x从小到大变化时,A由大变小,B由小变大。
容易发现这是一个下凸函数,可以用三分法求解。
但是,这道题卡精度(-11会WA,-12会T),解决方法是根据复杂度把循环次数卡到极限而不用r-l<=eps的方法,以及缩小一开始的l和r范围防卡。
#include<cstdio> #include<cstring> #include<cctype> #include<cmath> #include<queue> #include<stack> #include<set> #include<vector> #include<algorithm> #define ll long long #define lowbit(x) x&-x using namespace std; int read(){ char c;int s=0,t=1; while(!isdigit(c=getchar()))if(c=='-')t=-1; do{s=s*10+c-'0';}while(isdigit(c=getchar())); return s*t; } int ab(int x){return x>0?x:-x;} //int MO(int x){return x>=MOD?x-MOD:x;} //void insert(int u,int v){tot++;e[tot].v=v;e[tot].from=first[u];first[u]=tot;} /*------------------------------------------------------------*/ const int maxn=200010; const double eps=1e-9; int n; double a[maxn],b[maxn]; double F(double x){ for(int i=1;i<=n;i++)b[i]=a[i]-x; double sum=0,ans=0; for(int i=1;i<=n;i++){ sum+=b[i]; if(sum<0)sum=0; ans=max(ans,sum); } sum=0; for(int i=1;i<=n;i++){ sum+=b[i]; if(sum>0)sum=0; ans=max(ans,-sum); } return ans; } int main(){ n=read(); double m1,m2,l=-10010,r=10010; for(int i=1;i<=n;i++)scanf("%lf",&a[i]); for(int i=1;i<=100;i++){ m1=l+(r-l)/3;m2=l+(r-l)/3*2; if(F(m1)<F(m2))r=m2;else l=m1; } printf("%.10lf",F(l)); return 0; }
进一步观察,发现答案出现在A=B时,当x偏小时A>B,当x偏大时A<B,那么可以二分求解。
最后的几何解法,将max(si-sj)展开后化为以x为自变量,y为绝对值的一些直线,然后求上下凸包。