在一列礼物(v[1],v[2] …… v[n])中选择长度满足要求(L<=长度<=R)的一段连续区间中的礼物,使得这一段中礼物美观度的最大值与最小值之差,与长度的比值最大,求出这个最大的比值。
先用01分数规划,可以设[M(i,j)-m(i,j)]/(j-i+k)<=ans,整理得[M(i,j)-m(i,j)]-ans*(j-i+k)<=0,然后对ans做二分即可,重点是二分中的check()函数怎么写:
如果题目没有限制选出礼物的长度,为了使比值最大,选出的这一段中美观度最大与最小的礼物一定位于区间的两端。而由于题目限制了区间的长度,因此我们应当分两种情况考虑:
1:区间的长度恰好为L,此时的判定条件即为:[M(i,j)-m(i,j)]-ans*(l-1+k)<=0,预处理出整个礼物序列的st表,再O(n)枚举起始点,逐个判定即可。
2:区间的长度大于L,此时仍分两种情况讨论,即左端点为最大值或最小值的两种不同情况。分别求出(v[i]-i*mid)-(v[j]-j*mid)与(v[i]+i*mid)-(v[j]+j*mid)的最大值之后与零进行比较即可。
#include<iostream>
using namespace std;
const double eps=1e-7;
int T;
int m,n,L,R;
int pq1[51015],l1,r1;
int pq2[51015],l2,r2;
int pq3[51015],l3,r3;
int a[51015];
double b[51015];
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d%d%d",&m,&n,&L,&R);
for(int i=1;i<=m;i++){
scanf("%d",&a[i]);
}
l1=r1=l2=r2=0;
for(int i=1;i<L;i++){
while(l1<r1&&a[pq1[r1-1]]>=a[i]){
r1--;
}
while(l2<r2&&a[pq2[r2-1]]<=a[i]){
r2--;
}
pq1[r1++]=pq2[r2++]=i;
}
double ans1=-1000;
for(int i=L;i<=m;i++){
while(l1<r1&&i-pq1[l1]>=L){
l1++;
}
while(l2<r2&&i-pq2[