Description
著名出题人小Q的备忘录上共有n道可以出的题目,按照顺序依次编号为1到n,其中第i道题目的难度系数被小Q估计
为a_i,难度系数越高,题目越难,负数表示这道题目非常简单。小Q现在要出一套难题,他决定从备忘录中选取编
号连续的若干道题目,使得平均难度系数最高。当然,小Q不能做得太过分,一套题目必须至少包含k道题目,因此
他不能通过直接选取难度系数最高的那道题目来组成一套题。请写一个程序,帮助小Q挑选平均难度系数最高的题
目。
n,k(1<=n<=100000,1<=k<=n)
(|a_i|<=10^8)
输出一个既约分数p/q或-p/q,即平均难度系数的最大值。
Solution
考虑前缀和,那么问题变成求一对l和r使得
s[r]−s[l]r−l
s
[
r
]
−
s
[
l
]
r
−
l
最大
这是一个比较显然的分数规划问题,我们二分答案然后求长度至少为k的最大子段和即可
注意输出的时候负号的位置
Code
#include <stdio.h>
#include <string.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
typedef long long LL;
const double eps=0.000001;
const int N=200005;
double b[N];
LL s[N],a[N],up,down;
int n,m;
bool check(double mid) {
rep(i,1,n) b[i]=b[i-1]+a[i]-mid;
double min=0; int pos=0;
rep(i,m,n) {
if (b[i]-min>0) {
up=s[i]-s[pos];
down=i-pos;
return true;
}
if (b[i-m+1]<min) min=b[i-m+1],pos=i-m+1;
}
return false;
}
LL gcd(LL x,LL y) {
return !y?x:gcd(y,x%y);
}
int main(void) {
scanf("%d%d",&n,&m);
rep(i,1,n) scanf("%lld",&a[i]);
rep(i,1,n) s[i]=s[i-1]+a[i];
for (double l=-1e8,r=1e8;r-l>eps;) {
double mid=(l+r)*0.5;
if (check(mid)) l=mid;
else r=mid;
}
LL GCD=gcd(up,down);
if (GCD<0) GCD=-GCD;
printf("%lld/%lld\n", up/GCD,down/GCD);
return 0;
}