[NOIP2017模拟]最佳序列
2017-11-04 13:51
337 查看
2017.11.2 T2 2029
样例数据
输入
3 2 3
6 2 8
输出
5.3333
分析:这道题我只会扫描所有的区间输出答案这种暴力,也就是说复杂度是O((r−l)∗N),复杂度与区间有关,结果数据水,40%成功水成90%,而且如果进行优化,还能AC!
正解是单调队列优化:
二分平均数,将每个数都减去一个平均数,然后记录前缀和,如果在长为l到r的所有区间中有前缀和大于0的说明就可以到这个平均数,没有就说明不能到平均数,单调队列能让这个过程变成O(N)的。
代码
100%:暴力+优化
100%:单调队列优化
本题结。
样例数据
输入
3 2 3
6 2 8
输出
5.3333
分析:这道题我只会扫描所有的区间输出答案这种暴力,也就是说复杂度是O((r−l)∗N),复杂度与区间有关,结果数据水,40%成功水成90%,而且如果进行优化,还能AC!
正解是单调队列优化:
二分平均数,将每个数都减去一个平均数,然后记录前缀和,如果在长为l到r的所有区间中有前缀和大于0的说明就可以到这个平均数,没有就说明不能到平均数,单调队列能让这个过程变成O(N)的。
代码
100%:暴力+优化
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<ctime> #include<cmath> #include<algorithm> #include<cctype> #include<iomanip> #include<queue> #include<set> using namespace std; inline int getint() { int sum=0,f=1; char ch; for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar()); if(ch=='-') { f=-1; ch=getchar(); } for(;isdigit(ch);ch=getchar()) sum=(sum<<3)+(sum<<1)+ch-48; return sum*f; } const int maxn=20005; const double eps=1e-9; int n,l,r,a[maxn],maxa; double ans,sum[maxn]; inline void solve() { for(register int i=l;i<=min(l*2,r);++i)//这是个优化 //如果是一个数的区间,显然取最大的那个数就行,它和任意数取平均数都会比本身小 //同理,如果找到了长为l的平均最大的区间,显然与其他长为l的区间合并求平均数都会比他小,所以只需要求长为l到长为2l的区间的最大值就完了 //现在想来觉得有点小bug啊...... { if(sum[i]/i-ans>eps) ans=sum[i]/i; for(register int j=i+1;j<=n;++j) if((sum[j]-sum[j-i])/i-ans>eps) ans=(sum[j]-sum[j-i])/i; } printf("%0.4f\n",ans); } int main() { freopen("seq.in","r",stdin); freopen("seq.out","w",stdout); n=getint(),l=getint(),r=getint(); for(register int i=1;i<=n;++i) { a[i]=getint(); sum[i]=sum[i-1]+a[i];//记录前缀和 } solve(); return 0; }
100%:单调队列优化
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<ctime> #include<cmath> #include<algorithm> #include<cctype> #include<iomanip> #include<deque> #include<set> using namespace std; int getint() { int sum=0,f=1; char ch; for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar()); if(ch=='-') { f=-1; ch=getchar(); } for(;isdigit(ch);ch=getchar()) sum=(sum<<3)+(sum<<1)+ch-48; return sum*f; } const int maxn=20010; const double eps=1e-9; int n,l,r; double w,ans,a[maxn],maxa,b[maxn],sum[maxn]; bool check(double x) { for(int i=1;i<=n;++i) b[i]=a[i]-x;//把所有的数减去二分的这个平均数 for(int i=1;i<=n;++i) sum[i]=sum[i-1]+b[i];//记录前缀和 deque<int> que; for(int i=l;i<=r-1;++i)//先把长为l到r-1的前缀和放到数组里(只能是单调队列,小于的都删掉) { while(!que.empty()&&sum[i]>sum[que.back()]) que.pop_back(); que.push_back(i); } for(int i=1;i<=n-l+1;++i)//固定左端点 { while(!que.empty()&&que.front()<i+l-1) que.pop_front();//删除队顶与左端点相距小于的l的点(可能队列中还有,但是单调队列只关注最大的),保证队顶的数是满足在左端点右侧l到r的距离内的 if(i+r-1<=n) { while(!que.empty()&&sum[i+r-1]>sum[que.back()])//加入新的,把小于它的都删掉 que.pop_back(); que.push_back(i+r-1); } if(sum[que.front()]-sum[i-1]>=0)//判断队列中(队列中的都是满足在区间里的)最大的能否让前缀和大于0,满足了就return true return true; } return false; } int main() { freopen("seq.in","r",stdin); freopen("seq.out","w",stdout); n=getint(),l=getint(),r=getint(); for(int i=1;i<=n;++i) { scanf("%lf",&a[i]); if(a[i]-maxa>eps) maxa=a[i]; } double l=0,r=maxa,mid; while(r-l>eps)//二分查找平均数 { mid=(l+r)/2; if(check(mid)) l=mid,ans=mid; else r=mid; } printf("%0.4f\n",ans); return 0; }
本题结。
相关文章推荐
- 【NOIP模拟】 (11.2) T2最佳序列
- 【jzoj5231】【NOIP2017模拟A组模拟8.5】【序列问题】 【分治】
- [NOIP2017模拟]序列操作
- 【JZOJ5231】【NOIP2017模拟A组模拟8.5】序列问题
- jzoj5249 【NOIP2017提高A组模拟8.10】文本编辑器 (序列修改类问题,数据结构)
- 【jzoj5337】【NOIP2017提高A组模拟8.25】【夜莺与玫瑰】【莫比乌斯反演】
- [NOIP2017模拟][poj1091]跳蚤
- 拉格朗日插值法 【NOIP2017提高A组模拟10.6】Count
- 【JZOJ 5400】【NOIP2017提高A组模拟10.7】Repulsed
- 2017NOIP普及模拟赛题解报告
- [NOIP2017模拟]分玩具
- [NOIP模拟][状压dp][dfs序列][线段树]
- JZOJ 100026. 【NOIP2017提高A组模拟7.7】图
- NOIP 2017 全假模拟冲刺[By Hkd]
- [NOIP2017模拟]弹球
- [NOIP2017模拟]星星
- JZOJ100041. 【NOIP2017提高A组模拟7.12】列车调度
- JZOJ 100043. 【NOIP2017提高A组模拟7.13】第K小数
- JZOJ 100047. 【NOIP2017提高A组模拟7.14】基因变异
- 【jzoj5290】【NOIP2017提高组A组模拟8.17】【行程的交集】