bzoj 5090: 组题 凸包+二分

来源:互联网 发布:win7 64系统优化 编辑:程序博客网 时间:2024/06/03 05:24

题意

著名出题人小Q的备忘录上共有n道可以出的题目,按照顺序依次编号为1到n,其中第i道题目的难度系数被小Q估计为a_i,难度系数越高,题目越难,负数表示这道题目非常简单。小Q现在要出一套难题,他决定从备忘录中选取编号连续的若干道题目,使得平均难度系数最高。当然,小Q不能做得太过分,一套题目必须至少包含k道题目,因此他不能通过直接选取难度系数最高的那道题目来组成一套题。请写一个程序,帮助小Q挑选平均难度系数最高的题目。
输出一个既约分数p/q或-p/q,即平均难度系数的最大值。
1<=n<=100000,1<=k<=n,|a_i|<=10^8

分析

实际就是要求一个数对(i,j),满足i>j且i-j>k,并且(s[i]-s[j])/(i-j)最大。
注意到这就是一个斜率的形式,直接维护一个凸包然后在凸包上二分即可。
一开始wa是因为在gcd的时候没有考虑到会出现负数。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>using namespace std;typedef long long LL;const int N=100005;int n,k,q[N];LL s[N];int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}LL gcd(LL x,LL y){    if (!y) return x;    else return gcd(y,x%y);}int main(){    freopen("a.in","r",stdin);    n=read();k=read();    for (int i=1;i<=n;i++) s[i]=(LL)read()+s[i-1];    LL x=s[k],y=k;    int head=1,tail=1;q[1]=0;    for (int i=1;i<=n-k;i++)    {        while (head<tail&&(LL)(s[i]-s[q[tail]])*(q[tail]-q[tail-1])<=(LL)(s[q[tail]]-s[q[tail-1]])*(i-q[tail])) tail--;        q[++tail]=i;        int l=head,r=tail-1;        while (l<=r)        {            int mid=(l+r)/2;            if ((LL)(s[i+k]-s[q[mid]])*(i+k-q[mid+1])>(LL)(s[i+k]-s[q[mid+1]])*(i+k-q[mid])) r=mid-1;            else l=mid+1;        }        if ((LL)(s[i+k]-s[q[r+1]])*y>(LL)x*(i+k-q[r+1])) x=s[i+k]-s[q[r+1]],y=i+k-q[r+1];    }    LL d=gcd(x,y);if (d<0) d=-d;    printf("%lld/%lld",x/d,y/d);    return 0;}