BZOJ5090 组题 BZOJ2017年11月月赛 二分答案 单调队列

欢迎访问~原文出处——博客园-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;
}

  

上一篇:vue的双向数据绑定实现原理


下一篇:EF+jQueryUI前后端分离设计