题目摘要:
在一列礼物(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