Description
著名出题人小Q的备忘录上共有n道可以出的题目,按照顺序依次编号为1到n,其中第i道题目的难度系数被小Q估计为a_i,难度系数越高,题目越难,负数表示这道题目非常简单。小Q现在要出一套难题,他决定从备忘录中选取编
号连续的若干道题目,使得平均难度系数最高。当然,小Q不能做得太过分,一套题目必须至少包含k道题目,因此他不能通过直接选取难度系数最高的那道题目来组成一套题。请写一个程序,帮助小Q挑选平均难度系数最高的题
目。
题解:
感觉题解的做法好复杂啊……看到平均数就想分数规划,那么将问题转化为判定性问题。设 s[i] 为 a[i] 的前缀和,当前二分的答案为 mid ,那么我们的目标就是找到:
s[j]−s[i−1]j−(i−1)>=mid
移项得:
s[j]−j×mid−(s[i−1]−(i−1)×mid)>=0
那么只要每次求出前缀和,然后再减去下标乘
mid
,找有没有
j−i+1>=k
且
s[j]−s[i−1]>=0
就可以了。那么怎么找呢?这个也十分简单,设
mn[i]
为
0
到代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const long double eps=1e-7;
const int Maxn=100010;
const int inf=2147483647;
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<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
LL gcd(LL a,LL b){return((!b)?a:gcd(b,a%b));}
LL x,y,sum[Maxn];
int n,k,q[Maxn],mn[Maxn];
long double a[Maxn],s[Maxn],t;
bool check(long double v)
{
s[0]=mn[0]=0;
for(int i=1;i<=n;i++)s[i]=s[i-1]+a[i];
for(int i=1;i<=n;i++)s[i]-=v*(long double)(i);
for(int i=1;i<=n;i++)
{
if(s[i]<s[mn[i-1]])mn[i]=i;
else mn[i]=mn[i-1];
}
for(int i=k;i<=n;i++)
if(s[i]>=s[mn[i-k]]){y=i;x=mn[i-k];return true;}
return false;
}
int main()
{
n=read(),k=read();sum[0]=0;
for(int i=1;i<=n;i++)scanf("%Lf",&a[i]),sum[i]=sum[i-1]+(LL)(a[i]);
long double l=-100000000,r=100000000;
while(l+eps<=r)
{
long double mid=(l+r)/2.0;
if(check(mid))l=mid;
else r=mid;
}
l=(l+r)/2.0;int u=0;
if(l<0)l=-l,u=1;
LL t1,t2;
t1=(LL)(l*(long double)(y-x)+0.5);t2=y-x;
LL g=gcd(abs(t1),t2);
if(u)printf("-");printf("%lld/%lld",t1/g,t2/g);
}