您的位置:首页 > Web前端 > JavaScript

bzoj4476 [Jsoi2015]送礼物

2017-11-03 15:18 148 查看
化简式子

$M>=m+ans*(r-l+k)$

发现$M,m$确定时,总区间长度越小越好,于是假定右端点为最小值$M+ans*l>=m+ans*r+ans*k$,

右面都确定了,但最大值仍然有两种情况,一是最大值就在要求的区间内,二是在要求的区间右侧,

对于第一种情况,直接把每个点的val扔进单调队列就可以了,第二种呢,因为要求区间长度最小,所以左端点即为$r-L+1$,单调队列维护$i-L+1~i$的最大值即可,

左端点为最小值也一样。

而且不需要判断在我们选的区间内是否有比$a[i]$更小的,因为那一定比这优,考虑过,

所以每次check直接正反扫就好了。

2017.11.7更新

原程序被hack,因为如果只能全选的话,最小值又不在两边,就不会枚举到最优解,所以强行往两侧各添加n个0即可。

代码已更正,可放心食用

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define N 150050
#define eps 1e-6
using namespace std;
int n,l,r,k;
int a
;
double val
;
int q1
,q2
,head1,head2,tail1,tail2;
bool check(double x){
head1=head2=1;
tail1=tail2=0;
for(int i=1;i<=2*n;i++){
int pos=i-l+1;
double now=-1000000000;
if(pos>0){
val[pos]=a[pos]+x*pos;
while(head1<=tail1&&val[pos]>val[q1[tail1]])tail1--;
q1[++tail1]=pos;
while(head1<=tail1&&q1[head1]<=i-r)head1++;
now=max(now,val[q1[head1]]);
}
while(head2<=tail2&&a[i]>a[q2[tail2]])tail2--;
q2[++tail2]=i;
while(head2<=tail2&&q2[head2]<=i-l)head2++;
if(i>n&&pos>0){
now=max(now,a[q2[head2]]+x*(i-l+1));
if(now>=a[i]+x*(i+k))return 1;
}
}
head1=head2=1;
tail1=tail2=0;
for(int i=3*n;i>=n+1;i--){
int pos=i+l-1;
double now=-1000000000;
if(pos<=3*n){
val[pos]=a[pos]-x*pos;
while(head1<=tail1&&val[pos]>val[q1[tail1]])tail1--;
q1[++tail1]=pos;
while(head1<=tail1&&q1[head1]>=i+r)head1++;
now=max(now,val[q1[head1]]);
}
while(head2<=tail2&&a[i]>a[q2[tail2]])tail2--;
q2[++tail2]=i;
while(head2<=tail2&&q2[head2]>=i+l)head2++;
if(i<=2*n&&pos<=3*n){
now=max(now,a[q2[head2]]-x*(i+l-1));
if(now>=a[i]-x*(i-k))return 1;
}
}
return 0;
}
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d%d%d%d",&n,&k,&l,&r);
for(int i=n+1;i<=2*n;i++)scanf("%d",&a[i]);
double L=0,R=1000,M;
while(L+eps<R){
M=(L+R)/2.0;
if(check(M))L=M;
else R=M;
}
printf("%0.4lf\n",M);
}
return 0;
}


bzoj4476
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: