可以很简单地发现,如果可以找到这样的两点,那么最优的情况一定是,两个点中至少有一个点在端点上。
对于每一个斜率k,将第i个节点减去k*i,形成新的图。在新的图上斜率大于等于0就是符合条件的。
我们可以枚举右端点。对于同一个右端点,如果点a比点b更靠近右端点并且点a不小于点b,那么点a就不需要考虑了,因为点b一定比它更好。所以从所有小于右端点的点中取出一个单调递减的数组,二分查找出最大的不大于右端点的点作为左端点。
尝试将左端点往左移动1km,将右端点往右移动1km,找出最大值。
/* 首先有结论,最长的段肯定是一端在端点上 对于每个g,预处理:a[i]-i*g,这样只要a[j]-a[i]>=0,[i,j]就是满足要求的段 所以预处理出一个单调递减序列,对于每个a[i],在序列上找最右的比它大的即可 */ #include<bits/stdc++.h> using namespace std; #define N 200005 #define db double int n,h[N],stk[N],top; db g,a[N]; int main(){ cin>>n;int q;cin>>q; for(int i=0;i<=n;i++)scanf("%d",&h[i]); while(q--){ cin>>g;g*=10.0; for(int i=0;i<=n;i++)a[i]=h[i]-i*g; top=0; for(int i=1;i<=n;i++){ if(top==0){stk[++top]=i;continue;} while(top && a[stk[top]]<=a[i]) top--; stk[++top]=i; } db ans=0,last; for(int i=0;i<n;i++){ if(i!=0 && a[i]>last)continue; else last=a[i]; int L=1,R=top,mid,anss=-1; while(L<=R){ mid=L+R>>1; if(stk[mid]<=i) L=mid+1; else if(a[stk[mid]]>=a[i]) anss=stk[mid],L=mid+1; else R=mid-1; } if(i==0 && anss==n){ans=n;break;} if(anss!=-1){//[i,anss]这一段是合法的 db len1=0,len2=0; if(i!=0){ db L=0,R=1,mid; while(R-L>=1e-7){ mid=(L+R)/2; if(a[i]+mid*(a[i-1]-a[i])<=a[anss]) L=mid,len1=mid; else R=mid; } } if(anss!=n){ db L=0,R=1,mid; while(R-L>=1e-7){ mid=(L+R)/2; if(a[anss]+mid*(a[anss+1]-a[anss])>=a[i]) L=mid,len2=mid; else R=mid; } } ans=max(ans,1.0*anss-i+max(len1,len2)); } } if(ans==0) puts("impossible"); else printf("%.7lf ",ans); } }