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即可。
代码已更正,可放心食用
bzoj4476
$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
相关文章推荐
- bzoj4476: [Jsoi2015]送礼物 分数规划
- BZOJ4476 JSOI2015送礼物(分数规划+单调队列)
- bzoj4476[JSOI2015]送礼物
- bzoj 4476: [Jsoi2015]送礼物 二分答案+单调队列
- 【BZOJ4476&JSOI2015】送礼物(二分,RMQ)
- 【BZOJ4476】[Jsoi2015]送礼物 分数规划+RMQ
- [BZOJ]4476: [Jsoi2015]送礼物 分数规划+RMQ
- BZOJ_4476_[Jsoi2015]送礼物_01分数规划+单调队列
- 2015元宵节礼物——《刀塔传奇》骨骼动画查看器,开源啦!
- BZOJ4475 JSOI2015子集选取(动态规划)
- 【bzoj4488: [Jsoi2015]最大公约数】性质题
- 计算几何初步—【JSOI2015】投影面积
- BZOJ4475 [Jsoi2015]子集选取
- [bzoj4487][JSOI2015]染色问题
- 4484: [Jsoi2015]最小表示
- [bzoj4487][Jsoi2015]染色_容斥原理
- bzoj4484[Jsoi2015]最小表示 拓补排序+bitset
- 【JSOI2015】字符串树
- bzoj 4475: [Jsoi2015]子集选取 数学