[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;
}
本文来自博客园,作者:zhouhuanyi,转载请注明原文链接:https://www.cnblogs.com/zhouhuanyi/p/16983682.html