题目大意:给定一个环,要求在这个环上截取长度为偶数且在[L,R]区间内的一段,要求平均值最大
看到环果断倍增
看到平均值最大果断二分答案
看到长度[L,R]果断单调队列
对数组维护一个前缀和,对前缀和维护单调递增的单调队列
每扫过一个数sum[i],将sum[i-L]加入单调队列,再把距离i超过R的点删掉
长度为偶数?对奇数位置和偶数位置分别维护一个单调队列即可
每次找到大于0的子串之后记录一下分母再退出就行了
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 100100 #define EPS 1e-7 using namespace std; int n,L,R,max_num,a[M<<1]; long double sum[M<<1]; long long numerator,denominator; int q[2][M],r[2],h[2]; bool Judge(long double x) { int i; for(i=1;i<=n;i++) sum[i]=sum[i-1]+(a[i]-x); r[0]=r[1]=h[0]=h[1]=0; for(i=L;i<=n;i++) { int *q=::q[i&1],&r=::r[i&1],&h=::h[i&1],x=i-L; while( r-h>=1 && sum[q[r]]>sum[x] ) q[r--]=0; q[++r]=x; while( i-q[h+1]>R ) q[++h]=0; if(sum[i]-sum[q[h+1]]>=0) return denominator=i-q[h+1]; } return false; } long double Bisecion() { long double l=0,r=max_num; while(r-l>EPS) { long double mid=(l+r)/2; if( Judge(mid) ) l=mid; else r=mid; } return (l+r)/2; } int main() { int i; cin>>n>>L>>R; if(L&1) L++; if(R&1) R--; for(i=1;i<=n;i++) { scanf("%d",&a[i]); max_num=max(max_num,a[i]); a[i+n]=a[i]; } n<<=1; long double ans=Bisecion(); numerator=(long long)(ans*denominator+0.5); long long gcd=__gcd(numerator,denominator); numerator/=gcd; denominator/=gcd; if(denominator==1) cout<<numerator<<endl; else cout<<numerator<<'/'<<denominator<<endl; return 0; }
BZOJ 3316 JC loves Mkk 二分答案+单调队列
原文地址:http://blog.csdn.net/popoqqq/article/details/43059527