欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ5090 11月月赛A题
题意概括
给出n个数。
求连续区间(长度大于等于k)最大平均值。
题解
这题大概不是原题。
很简单的题目(对于大佬而不对于我来说),做过一次。
具体做法:
首先二分答案平均值(最好用long double保证精度)
然后根据前缀和来单调队列判断。
假设当前要判断的答案为x。
我们把原序列的每一个数都减去x。
那么前缀和数组的第i个就减掉了i*x
那么我得到了一个新的前缀和数组(long double型)。
如果原序列存在平均值大于x的,那么修改后的序列必然存在总和大于等于0的连续一段数(当然长度要大于或等于k)
那么只需要看是否有距离>=k的两个前缀和的值满足前面一个小于后面一个就可以了。
这个可以用单调队列维护解决。
剩下的就是答案及细节处理。注意开longlong
代码
#include <cstring> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> using namespace std; typedef long long LL; typedef long double LD; const int N=100005; const LD Eps=1e-9; int n,k,ansL,ansR; int q[N],head,tail; LL sum[N],A,B; LD val[N]; LL gcd(LL a,LL b){ return b?gcd(b,a%b):a; } bool check(LD x){ for (int i=0;i<=n;i++) val[i]=-x*i+sum[i]; head=1,tail=0; for (int i=k;i<=n;i++){ while (head<=tail&&val[i-k]<val[q[tail]]) tail--; q[++tail]=i-k; if (head<=tail&&val[q[head]]<val[i]){ ansL=q[head],ansR=i; return 1; } } return 0; } int main(){ scanf("%d%d",&n,&k); sum[0]=0; for (int i=1;i<=n;i++) scanf("%lld",&sum[i]),sum[i]+=sum[i-1]; LD L=-1e8,R=1e8,M; while (R-L>Eps){ M=(R+L)*0.5; if (check(M)) L=M; else R=M; } A=sum[ansR]-sum[ansL]; B=ansR-ansL; LL g=gcd(A,B); A/=g,B/=g; if (B<0) A=-A,B=-B; printf("%lld/%lld",A,B); return 0; }