首页 > 代码库 > BZOJ 3316 JC loves Mkk 二分答案+单调队列

BZOJ 3316 JC loves Mkk 二分答案+单调队列

题目大意:给定一个环,要求在这个环上截取长度为偶数且在[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 二分答案+单调队列