BZOJ-5090 (二分答案)

题目

Problem A: 组题
Time Limit: 1 Sec Memory Limit: 256 MB

Description

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

Input

第一行包含两个整数n,k(1<=n<=100000,1<=k<=n),分别表示题目的总量和题数的下界。

第二行包含n个整数a_1,a_2,…,a_n(|a_i|<=10^8),分别表示每道题目的难度系数。

Output

输出一个既约分数p/q或-p/q,即平均难度系数的最大值。

Sample Input

5 3
1 4 -2 -3 6

Sample Output

5/4

分析

  • 看到这个“最大值”,又想到二分答案,想想复杂度、实现好像都可以,于是就想出来了。
  • 二分出答案后,对每个数减去二分的 mid,只需找到连续的一段和(减过 mid 之后的数)大于0,就说明这个 mid 可行。
  • 用个前缀和维护一下,得到可行解的时候记一下分子分母。
  • 最后输出即可。

程序

#include <cstdio>
long long n,k,o,mc,a1,a2,kk1,kk2,a[100005],f[100005];
long double ans,l,r,Mid,mn,s[100005];
long long gcd(long long b,long long c){
   
   return c?gcd(c,b%c):b;}

bool ok(long double x){
    long double mn=1000000;
    for (long long i=1; i<k; i++) s[i]=s[i-1]+(long double)a[i]-x;
    for (long long i=k; i<=n; i++){
        s[i]=s[i-1]+(long double)a[i]-x;
        if (s[i-k]<mn) mn=s[i-k],mc=i-k;
        if (s[i]-mn>=0){
            a1=f[i]-f[mc];
            a2=i-mc;
            return 1;
        }
    }
    return 0;
}

int main(){
    scanf("%lld%lld",&n,&k);
    for (long long i=1; i<=n; i++) scanf("%lld",&a[i]),f[i]=f[i-1]+a[i];
    for (l=-100000000,r=-l,o=0; l<=r && ++o<=100; o++){
        Mid=(l+r)/2;
        if (ok(Mid)){l=Mid; ans=Mid; continue;}
        r=Mid-0.000001;
    }
    k=gcd(a1,a2); a1/=k,a2/=k;
    if (a2<0) a1*=-1,a2*=-1;
    //if (a2==1){printf("%lld",a1); return 0;}
    printf("%lld/%lld",a1,a2);
}

提示

  • 要看清数据范围,开始二分的初值弄成 1e5 结果 WA 了 11 发……
  • 还有注意是既约分数, gcd 一下,负号位置注意一下。

猜你喜欢

转载自blog.csdn.net/jackypigpig/article/details/78756112
今日推荐