Loading

[JSOI2015]送礼物

链接:https://www.luogu.com.cn/problem/P6087

题目描述:求出\(L<=j-i<=R\)的点对\((i,j)\)\(\frac{max(a_{i}...a_{j})-min(a_{i}...a_{j})}{j-i+K}\)的最大值。

题解:我只听说过\(0/1\)分数规划,没听说过分数规划,这道题告诉我们所有分数都可以规划。

可以将原式二分分数规划,就有:\(max(a_{i}...a_{j})-min(a_{i}...a_{j})>=(j-i+K)\times L\)

则有\((max(a_{i}...a_{j})-j\times L)-(min(a_{i}...a_{j})-i\times L)>=K\times L\)

那么我们可以最大化\((max(a_{i}...a_{j})-j\times L)-(min(a_{i}...a_{j})-i\times L)\)的值。

把原问题缩放得我们就是在最大化\((a_{j}-j\times L)-(a_{i}-i\times L)\)的值。

\(b_{i}=a_{i}-i\times L\),则可以用单调队列求出\(b_{i}-b_{j}\)的最大值。

当然,如果你真的这么做的话会\(wa\)掉,然后你会惊奇的发现每一个测试点\(wa\)的都是最后一组数据。

我们漏了什么嘛?缩放后的范围会改变。

也就是说会存在一组最大值与最小值组成的区间\((i,j)\)不合法,但存在一个\(i'\)满足\((i',j)\)合法。

这样,我们可以求出所有\(j-i<L\)的情况,然后按为\(L\)的情况算就行了。

#include<iostream>
#include<cstdio>
using namespace std;
int deque[100001],l2,r2,a[100001],n,k,l,r;
double first,last,mid,b[100001];
bool check(double L)
{
	double ans=-1e9;
	for (int i=1;i<=n;++i)
		b[i]=a[i]-L*i;
	l2=1;
	r2=0;
	for (int i=1;i<=n-l+1;++i)
	{
		while (l2<=r2&&i+l-1-deque[l2]>r-1)
			l2++;
		while (l2<=r2&&b[deque[r2]]>=b[i])
			r2--;
		deque[++r2]=i;
		if (l2<=r2)
			ans=max(ans,b[i+l-1]-b[deque[l2]]);
	}
	for (int i=1;i<=n;++i)
		b[i]=a[n-i+1]-L*i;
	l2=1;
	r2=0;
	for (int i=1;i<=n-l+1;++i)
	{
		while (l2<=r2&&i+l-1-deque[l2]>r-1)
			l2++;
		while (l2<=r2&&b[deque[r2]]>=b[i])
			r2--;
		deque[++r2]=i;
		if (l2<=r2)
			ans=max(ans,b[i+l-1]-b[deque[l2]]);
	}
	return ans>=k*L;
}
void work()
{
	first=0;
	last=1e3;
	cin>>n>>k>>l>>r;
	for (int i=1;i<=n;++i)
		cin>>a[i];
	while (first+1e-6<=last)
	{
		mid=(first+last)/2;
		if (check(mid))
			first=mid;
		else
			last=mid;
	}
	l2=1;
	r2=0;
	for (int i=1;i<=n;++i)
	{
		while (i-deque[l2]>l-1)
			l2++;
		if (l2<=r2)
			mid=max(mid,1.0*(a[i]-a[deque[l2]])/(l+k-1));
		while (a[deque[r2]]>=a[i])
			r2--;
		deque[++r2]=i;
	}
	printf("%0.4lf\n",mid);
}
int main()
{
	int t;
	cin>>t;
	while (t--)
		work();
	return 0;
}
posted @ 2022-12-14 21:41  zhouhuanyi  阅读(22)  评论(0编辑  收藏  举报