【CodeForces】578 C. Weakness and Poorness

【题目】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;
}
View Code

 

进一步观察,发现答案出现在A=B时,当x偏小时A>B,当x偏大时A<B,那么可以二分求解。

最后的几何解法,将max(si-sj)展开后化为以x为自变量,y为绝对值的一些直线,然后求上下凸包。

 

 
posted @ 2017-12-08 14:40  ONION_CYC  阅读(244)  评论(0编辑  收藏  举报