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

bzoj4476[JSOI2015]送礼物

2017-03-07 16:59 148 查看
选择一段区间之后,对答案有影响的只是区间中的最大最小值,因此考虑枚举最大值找最优的最小值.(不需要保证这两个数值是对应的区间内的实际最大最小值,实际最大最小值对答案产生的贡献一定也会另外被枚举到).

由对称性,只考虑最小值在最大值左边的情况.对于下标等于x的数,如果让下标在[x-l+1,x-1]内的数字作为对应的最小值,那么必然需要延伸这个区间使得区间长度达到l.那么对于这种情况,我们查询这个下标范围内的最小数值即可(每个数值对应的区间长度相同).如果让下标和x的差值大于l小于r的数字作为最小值,则不同的数值对应不同的区间长度,且贪心可得对应的区间一定是一端为最大值,一段为最小值.分数规划,二分答案+单调队列判定即可.二分精度要高一点才不会WA….

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=50005;
int a[maxn],st[maxn][20];
int buf[maxn];
int n,k,l,r;
int rmq(int l,int r){
if(l>n||r<1)return 0x7f7f7f7f;
if(l<1)l=1;
if(r>n)r=n;
int j=buf[r-l+1];
return min(st[l][j],st[r-(1<<j)+1][j]);
}
int q[maxn];
bool check(double ans){
int head=0,tail=0;
for(int i=1;i<=n-l;++i){
while(head!=tail&&i+l>q[head]+r-1)head++;
while(head!=tail&&(a[q[tail-1]]-q[tail-1]*ans)>(a[i]-i*ans))tail--;
q[tail++]=i;
if(a[i+l]-(i+l+k)*ans>a[q[head]]-q[head]*ans)return true;
}
head=0;tail=0;
for(int i=n;i>l;--i){
while(head!=tail&&i-l<q[head]-r+1)head++;
while(head!=tail&&(a[q[tail-1]]+(q[tail-1]+k)*ans>a[i]+(i+k)*ans))tail--;
q[tail++]=i;
if(a[i-l]+(i-l)*ans>a[q[head]]+(q[head]+k)*ans)return true;
}
return false;
}
int main(){
int tests;scanf("%d",&tests);
for(int j=0;(1<<j)<maxn;++j)buf[1<<j]=j;
for(int i=3;i<maxn;++i){
if(!buf[i])buf[i]=buf[i-1];
}
while(tests--){
scanf("%d%d%d%d",&n,&k,&l,&r);
for(int i=1;i<=n;++i)scanf("%d",&a[i]);
for(int i=1;i<=n;++i)st[i][0]=a[i];
for(int j=1;(1<<j)<=n;++j){
for(int i=1;i<=n;++i){
st[i][j]=st[i][j-1];
if(i+(1<<j-1)<=n&&st[i+(1<<j-1)][j-1]<st[i][j])st[i][j]=st[i+(1<<j-1)][j-1];
}
}
double ans=0;
for(int i=1;i<=n;++i){
ans=max(ans,(a[i]-min(rmq(i+1,i+l-1),rmq(i-l+1,i-1)))/double(l-1+k));
}
double l=0,r=1e4;
while(r-l>1e-7){
double mid=(l+r)/2.0;
if(check(mid))l=mid;
else r=mid;
}
printf("%.4f\n",max(ans,l));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: