[bzoj4476]送礼物
题解是二分加单调队列来着。。。。
长度等于l的直接rmq。
我们考虑到我们选择两个点为最大最小的最优策略就是这两个点为边界的区间,然后题目就是要求两个距离在[L,R]之间的点然后让式子最大。然后就维护凸壳二分找答案就行了。
- 代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e4+5;
int L,R,n,k,a[N];
struct node{int y,x;}dat[N];
node stk[N];
int tl,tp;
double calc(node a,node b){
double deltx=b.x-a.x,delty=b.y-a.y;
return delty/deltx;
}
double ans=0;
inline void solve(){
int tl=1,tp=0;
for(int t=L;t<=n;t++){
node nw=(node){dat[t-L+1].y,dat[t-L+1].x};
while(tp>tl&& calc(stk[tp-1],stk[tp]) > calc(stk[tp-1],nw) )tp--;
stk[++tp]=nw;
node u=(node){dat[t].y,dat[t].x+k};
while(tl<tp&&stk[tl].x+R<=t)tl++;
int l=tl,r=tp;
while(l<r){
int mid=(l+r)>>1;
double anscur=calc(stk[mid],u);
double K=calc(stk[mid],stk[mid+1]);
if(K<anscur) l=mid+1;
else r=mid;
}
int pos=l;double newans=calc(stk[pos],u);
ans=max(newans,ans);
}
}
int MN[N][20],MX[N][20];
void ST_setup()//
{
memset(MN,0,sizeof(MN));memset(MX,0,sizeof(MX));
for(int i=1;i<=n;i++)MN[i][0]=MX[i][0]=a[i];
for(int i=1;(1<<i)<=n;i++)
for(int j=1;j<=n-(1<<i)+1;j++){
MX[j][i]=max(MX[j][i-1],MX[j+(1<<(i-1))][i-1]);//
MN[j][i]=min(MN[j][i-1],MN[j+(1<<(i-1))][i-1]);//
}
return;
}
int ST_search(int l,int r){
int k=(int)log2(r-l+1);
return max(MX[l][k],MX[r+1-(1<<k)][k])-min(MN[l][k],MN[r+1-(1<<k)][k]);
}
int main()
{
int T;scanf("%d",&T);
while(T--){
ans=0.00;
scanf("%d%d%d%d",&n,&k,&L,&R);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
ST_setup();
for(int i=1;i<=n;i++){
int r=i+L-1;
if(r>n)continue;
ans=max(ans,(double)((double)ST_search(i,r)/(double)(L-1+k)));
}
for(int i=1;i<=n;++i) dat[i].y=a[i],dat[i].x=i;
solve();
for(int i=1;i<=n;++i) dat[i].y=a[n-i+1],dat[i].x=i;
solve();
printf("%.4lf\n",ans);
}
}