【NOIP模拟】 (11.2) T2最佳序列
2017-11-03 07:50
393 查看
最佳序列
题目描述:N个数,从中选择长度不小于L且不大于R的连续子序列,求出子序列平均数的最大值。
输入格式:
输入文件的第一行包括3个整数N,L,R。
第二行包括N个数,按顺序依次表示序列A的每一项。
输出格式:
输出文件包括一行,一个实数,保留四位小数。
数据范围:
20%的数据满足:1<=N<=200;R=N。
40%的数据满足:1<=N<=2000。
100%的数据满足:1<=n<=20000;1<=L<=R<=N;0<=Ai<=1000000
解析:
对于20%的数据,暴力枚举即可。
对于40%的数据,可以用前缀和的方式降维(但这道题在暴力进行优化后可以拿满......)。
对于100%的数据,二分答案是很好想到的,但这道题的关键在于如何验证。于是我们要进行推理:
假设这时我们二分到的平均数为x,那么如果满足条件,在这个序列中一定有一个长度为L~R的子序列满足平均数大于等于x,如果用暴力搜索肯定超时,所以我们来思考一下若当前搜索到了i作为右端点,左端点一定在i-R+1~i-L+1这个区间里,如果满足条件,那么
sum[i]-sum[i-R]>=(i-R)*x,sum[i]-sum[i-L]>=(i-L)*x(sum为前缀和)
我们把等式右边拆开移到左边就变成了
sum[i]-i*x-(sum[i-R]-R*x)>=0,右边的不等式同理
拆开后相当于
sum[i]'-sum[i-R]'>=0,右边的不等式同理
所以我们只用将sum都减去x,再计算出的sum[i-R]~sum[i-L]中如果最小的sum[j]能使得不等式成立,说明满足提议,x是存在的,否则是不存在的。那么问题来了,如何求出在一个长度固定的区间求最小的值,这里出现了两种方法:一种是线段树,一种是单调队列。
线段树很好想到,因为这个问题是一个很裸的模板题,将sum减去x后存进线段树,然后枚举i时进行查找最小值即可。时间复杂度O(N (log N)^2)。
这道题能用单调队列的原因是满足长度固定,而且每次取最小值,满足单调性,所以可以用单调队列实现。时间复杂度O(N log N)
代码(暴力100分....)
#include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> #include <cctype> #include <cstring> #include <string> #include <vector> #include <queue> #include <ctime> #include <sstream> using namespace std; const int Max=200010; int n,L,R; double l,r,mid; double ans; long long num[Max],sum[Max]; inline int get_int() { int x=0,f=1; char c; for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar()); if(c=='-') {f=-1;c=getchar();} for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0'; return x*f; } inline int check() { for(register int i=L;i<=R;i++) { long long maxx=0; for(register int j=i;j<=n;j++) maxx=max(sum[j]-sum[j-i],maxx); ans=max((double)(maxx)/(double)(i),ans); } } int main() { //freopen("seq.in","r",stdin); //freopen("seq.out","w",stdout); n=get_int(); L=get_int(); R=get_int(); for(register int i=1;i<=n;i++) num[i]=get_int(); for(register int i=1;i<=n;i++) sum[i]=sum[i-1]+num[i]; l=0; r=1e7; check(); printf("%0.4f\n",ans); return 0; }
代码(二分+单调队列):
#include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> #include <cctype> #include <cstring> #include <string> #include <vector> #include <queue> #include <ctime> #include <sstream> using namespace std; const int Max=200010; int n,L,R; double l,r,mid; int maxx; int num[Max]; double sum[Max]; inline int get_int() { int x=0,f=1; char c; for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar()); if(c=='-') {f=-1;c=getchar();} for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0'; return x*f; } inline int check(double x) { static int p[Max]; for(int i=1;i<=n;i++) sum[i]=(double)num[i]-x; for(int i=1;i<=n;i++) sum[i]+=sum[i-1]; int head=1,tail=0; for(int i=L;i<=n;i++) //注意要从L开始而不能从L+1开始,不然会漏算1~L的情况 { while(head<=tail&&sum[p[tail]]>=sum[i-L]) tail--; p[++tail]=i-L; while(head<=tail&&i-R>p[head]) head++; if(sum[p[head]]<=sum[i]) return 1; } return 0; } int main() { //freopen("seq.in","r",stdin); // freopen("seq.out","w",stdout); n=get_int(); L=get_int(); R=get_int(); for(int i=1;i<=n;i++) {num[i]=get_int();maxx=max(maxx,num[i]);} l=0; r=maxx; for(int i=1;i<=50;i++) { mid=(l+r)/2; if(check(mid)) l=mid; else r=mid; } printf("%0.4f\n",l); return 0; }
代码(二分+线段树)
#include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cmath> #include <cctype> #include <cstring> #include <string> #include <vector> #include <queue> #include <ctime> #include <sstream> using namespace std; const int MAX=1e9; const int Max=200010; int n,L,R; double l,r,mid; int maxx; int num[Max]; double sum[Max]; struct shu{int l,r;double minn;}; shu tree[Max*4]; inline int get_int() { int x=0,f=1; char c; for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar()); if(c=='-') {f=-1;c=getchar();} for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0'; return x*f; } inline void build(int root,int L,int R) { tree[root].l=L; tree[root].r=R; if(L==R) { tree[root].minn=sum[L]; return; } int mid=(L+R)>>1; build(root<<1,L,mid); build(root<<1|1,mid+1,R); tree[root].minn=min(tree[root<<1].minn,tree[root<<1|1].minn); } inline double Q(int root,int L,int R,int l,int r) { if(l>R||r<L) return MAX; if(l<=L&&r>=R) { return tree[root].minn; } int mid=(L+R)>>1; if(r<=mid) return Q(root<<1,L,mid,l,r); else if(l>mid) return Q(root<<1|1,mid+1,R,l,r); else return min(Q(root<<1,L,mid,l,r),Q(root<<1|1,mid+1,R,l,r)); } inline int check(double x) { memset(tree,0,sizeof(tree)); static int p[Max]; for(register int i=1;i<=n;i++) sum[i]=(double)num[i]-x; for(register int i=1;i<=n;i++) sum[i]+=sum[i-1]; build(1,0,n); for(register int i=L+1;i<=n;i++) { if(Q(1,0,n,max(i-R,0),i-L)<=sum[i]) return 1; } return 0; } int main() { //freopen("seq.in","r",stdin); // freopen("seq.out","w",stdout); n=get_int(); L=get_int(); R=get_int(); for(register int i=1;i<=n;i++) {num[i]=get_int();maxx=max(maxx,num[i]);} l=0; r=maxx; for(register int i=1;i<=50;i++) { mid=(l+r)/2; if(check(mid)) l=mid; else r=mid; } printf("%0.4f\n",l); return 0; }
相关文章推荐
- [NOIP2017模拟]最佳序列
- [NOIp2017 Day1 T2] 时间复杂度complexity(栈,模拟)
- jzoj3518【NOIP2013模拟11.6A组】进化序列
- NOIP模拟(20171102)T2 最佳序列
- [NOIP2013]表达式求值 T2 模拟
- NOIP模拟(10.26)T2 做运动
- 【NOIP 模拟题】T2(next数组|线段树+最长上升子序列)
- POJ-1091:[NOIP模拟](二)T2-跳蚤
- 【NOIP2015模拟11.2】复制&粘贴2
- 【NOIP2015模拟11.2】有趣的有趣的家庭菜园
- jzoj. 3889. 【NOIP2014模拟10.25B组】序列问题
- 【NOIP2015 11.2上午模拟】总结
- NOIPの模拟_2016_8_11_t2_种树
- JZOJ 3518. 【NOIP2013模拟11.6A组】进化序列(evolve)
- [NOIP模拟][状压dp][dfs序列][线段树]
- 【NOIP 模拟赛】平均数 涂色游戏 序列题解
- NOIP11.15模拟 T2 三部曲
- [jzoj3889]【NOIP2014模拟10.25B组】序列问题
- 【JZOJ5231】【NOIP2017模拟A组模拟8.5】序列问题
- NOIP模拟 序列操作【线段树】