bzoj4476 [Jsoi2015]送礼物

化简式子

$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即可。

代码已更正,可放心食用

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #include<cmath>
 6 #define N 150050
 7 #define eps 1e-6
 8 using namespace std;
 9 int n,l,r,k;
10 int a[N];
11 double val[N];
12 int q1[N],q2[N],head1,head2,tail1,tail2;
13 bool check(double x){
14     head1=head2=1;
15     tail1=tail2=0;
16     for(int i=1;i<=2*n;i++){
17         int pos=i-l+1;
18         double now=-1000000000;
19         if(pos>0){
20             val[pos]=a[pos]+x*pos;
21             while(head1<=tail1&&val[pos]>val[q1[tail1]])tail1--;
22             q1[++tail1]=pos;
23             while(head1<=tail1&&q1[head1]<=i-r)head1++;
24             now=max(now,val[q1[head1]]);
25         }
26         while(head2<=tail2&&a[i]>a[q2[tail2]])tail2--;
27         q2[++tail2]=i;
28         while(head2<=tail2&&q2[head2]<=i-l)head2++;
29         if(i>n&&pos>0){
30             now=max(now,a[q2[head2]]+x*(i-l+1));
31             if(now>=a[i]+x*(i+k))return 1;
32         }
33     }
34     head1=head2=1;
35     tail1=tail2=0;
36     for(int i=3*n;i>=n+1;i--){
37         int pos=i+l-1;
38         double now=-1000000000;
39         if(pos<=3*n){
40             val[pos]=a[pos]-x*pos;
41             while(head1<=tail1&&val[pos]>val[q1[tail1]])tail1--;
42             q1[++tail1]=pos;
43             while(head1<=tail1&&q1[head1]>=i+r)head1++;
44             now=max(now,val[q1[head1]]);
45         }
46         while(head2<=tail2&&a[i]>a[q2[tail2]])tail2--;
47         q2[++tail2]=i;
48         while(head2<=tail2&&q2[head2]>=i+l)head2++;
49         if(i<=2*n&&pos<=3*n){
50             now=max(now,a[q2[head2]]-x*(i+l-1));
51             if(now>=a[i]-x*(i-k))return 1;
52         }
53     }
54     return 0;
55 }
56 int main(){
57     int T;scanf("%d",&T);
58     while(T--){
59         scanf("%d%d%d%d",&n,&k,&l,&r);
60         for(int i=n+1;i<=2*n;i++)scanf("%d",&a[i]);
61         double L=0,R=1000,M;
62         while(L+eps<R){
63             M=(L+R)/2.0;
64             if(check(M))L=M;
65             else R=M;
66         }
67         printf("%0.4lf\n",M);
68     }
69     return 0;
70 }
bzoj4476
posted @ 2017-11-03 15:18  Ren_Ivan  阅读(157)  评论(0编辑  收藏  举报