【题目】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为绝对值的一些直线,然后求上下凸包。