网络赛 I题 Max answer 单调栈+线段树
题目链接:https://nanti.jisuanke.com/t/38228
题意:在给出的序列里面找一个区间,使区间最小值乘以区间和得到的值最大,输出这个最大值。
思路:我们枚举每一个数字,假设是a[i],那么我们就要找一个包含a[i]的区间,并且这个区间里面的最小值就是a[i],使a[i]乘以这个区间的区间和最大,一直更新这个最大值就可以了。
要保证区间最小值为a[i],那么就要找下标i左边第一个小于a[i]的数字所在下标和右边第一个小于a[i]的数字下标,我们在这两个下标围成的区间里面找最优的区间和,这个用单调栈,线段树什么的都可以做到,这里用单调栈,因为最快,是线性的。求出每一个数字左右两边第一个比他小的数字下标。L[i]表示左边第一个比a[i]小的数字下标,R[i]表示右边第一个比a[i]小的数字下标,s是一个栈,里面存的是下标。
代码:
- //单调栈找每个数字左右两边比自己小的数字位置
- for(int i=;i<=n;i++){
- while(!s.empty()&&a[s.top()]>a[i]){//在保证栈不为空的情况下把栈顶大于a[i]的
- //元素弹出,并把R[s.top()]赋值为i
- R[s.top()]=i;
- s.pop();
- }
- if(!s.empty()){//如果栈不为空,那么栈顶有比a[i]小的数字
- if(a[s.top()]!=a[i])//如果这个栈顶数字不是a[i],L[i]=s.top()
- L[i]=s.top();
- else //如果栈顶数字也是自己,那么向右递推,L[i]=L[s.top()]
- L[i]=L[s.top()];
- }
- else
- L[i]=;//栈为空,没有小于a[i]的数字
- s.push(i);//把当前数字压入栈
- }
- while(!s.empty()){
- R[s.top()]=n+;
- s.pop();
- }
我们用前缀和建线段树,一个叶子节点代表一个前缀和。
如果a[i]是一个正数,那么这个区间就要是a[i]为区间最小值,并且区间和尽量大。我们在区间[L[i],i-1]里找一个最小的前缀和,在区间[i,R[i]-1]里找一个最大的前缀和,用最大的减最小的就得到了包含a[i]的最大区间和。
如果a[i]是一个负数,那么这个区间就要是a[i]为区间最小值,并且区间和尽量小。我们在区间[L[i],i-1]里找一个最大的前缀和,在区间[i,R[i]-1]里找一个最小的前缀和,用最小的减最大的就是包含a[i]的最小区间和。
然后一直更新就可以了,最后注意n最大是5乘10的5次方...
代码:
- #include<iostream>
- #include<cstring>
- #include<algorithm>
- #include<queue>
- #include<map>
- #include<stack>
- #include<cmath>
- #include<vector>
- #include<set>
- #include<cstdio>
- #include<string>
- #include<deque>
- using namespace std;
- typedef long long LL;
- #define eps 1e-8
- #define INF 0xfffffffffffff
- #define maxn 500005
- int a[maxn],L[maxn],R[maxn];
- LL sum[maxn];
- int n,m,k,t;
- stack<int>s;
- struct node{
- LL Max,Min;
- }tree[maxn<<];
- void update(int k){
- tree[k].Max=max(tree[k<<].Max,tree[k<<|].Max);
- tree[k].Min=min(tree[k<<].Min,tree[k<<|].Min);
- }
- void build(int l,int r,int k){//建树
- if(l==r){
- tree[k].Max=tree[k].Min=sum[l];
- return;
- }
- int mid=(l+r)/;
- build(l,mid,k<<);
- build(mid+,r,k<<|);
- update(k);
- }
- LL ask_Max(int l,int r,int k,int L,int R){
- if(l>=L&&r<=R){
- return tree[k].Max;
- }
- int mid=(l+r)/;
- LL ans=-INF;
- if(L<=mid)
- ans=max(ans,ask_Max(l,mid,k<<,L,R));
- if(R>mid)
- ans=max(ans,ask_Max(mid+,r,k<<|,L,R));
- return ans;
- }
- LL ask_Min(int l,int r,int k,int L,int R){
- if(l>=L&&r<=R){
- return tree[k].Min;
- }
- int mid=(l+r)/;
- LL ans=INF;
- if(L<=mid)
- ans=min(ans,ask_Min(l,mid,k<<,L,R));
- if(R>mid)
- ans=min(ans,ask_Min(mid+,r,k<<|,L,R));
- return ans;
- }
- int main()
- {
- while(scanf("%d",&n)!=EOF){
- while(!s.empty())
- s.pop();
- sum[]=;
- for(int i=;i<=n;i++){
- scanf("%d",&a[i]);
- sum[i]=sum[i-]+a[i];
- }
- //单调栈找每个数字左右两边比自己小的数字位置
- for(int i=;i<=n;i++){
- while(!s.empty()&&a[s.top()]>a[i]){//在保证栈不为空的情况下把栈顶大于a[i]的
- //元素弹出,并把R[s.top()]赋值为i
- R[s.top()]=i;
- s.pop();
- }
- if(!s.empty()){//如果栈不为空,那么栈顶有比a[i]小的数字
- if(a[s.top()]!=a[i])//如果这个栈顶数字不是a[i],L[i]=s.top()
- L[i]=s.top();
- else //如果栈顶数字也是自己,那么递推,L[i]=L[s.top()]
- L[i]=L[s.top()];
- }
- else
- L[i]=;//栈为空,没有小于a[i]的数字
- s.push(i);//把当前数字压入栈
- }
- while(!s.empty()){
- R[s.top()]=n+;
- s.pop();
- }
- build(,n,);
- LL ans=-INF;
- for(int i=;i<=n;i++){
- if(a[i]>=){//a[i]大于等于0,求左边最小的前缀和,右边最大的前缀和,右边减左边,得到包含a[i]的最大区间和
- LL Min=ask_Min(,n,,L[i],i-);
- LL Max=ask_Max(,n,,i,R[i]-);
- ans=max(ans,a[i]*(Max-Min));
- }else{//a[i]小于0,求左边最大的前缀和,右边最小的前缀和,右边减左边,得到包含a[i]的最小区间和
- LL Min=ask_Min(,n,,i,R[i]-);
- LL Max=ask_Max(,n,,L[i],i-);
- ans=max(ans,a[i]*(Min-Max));
- }
- }
- printf("%lld\n",ans);
- }
- return ;
- }
网络赛 I题 Max answer 单调栈+线段树的更多相关文章
- 2019ICPC南昌邀请赛网络赛 I. Max answer (单调栈+线段树/笛卡尔树)
题目链接 题意:求一个序列的最大的(区间最小值*区间和) 线段树做法:用单调栈求出每个数两边比它大的左右边界,然后用线段树求出每段区间的和sum.最小前缀lsum.最小后缀rsum,枚举每个数a[i] ...
- 南昌邀请赛I.Max answer 单调栈+线段树
题目链接:https://nanti.jisuanke.com/t/38228 Alice has a magic array. She suggests that the value of a in ...
- The Preliminary Contest for ICPC China Nanchang National Invitational I. Max answer (单调栈+线段树)
题目链接:https://nanti.jisuanke.com/t/38228 题目大意:一个区间的值等于该区间的和乘以区间的最小值.给出一个含有n个数的序列(序列的值有正有负),找到该序列的区间最大 ...
- 洛谷P4198 楼房重建 单调栈+线段树
正解:单调栈+线段树 解题报告: 传送门! 首先考虑不修改的话就是个单调栈板子题昂,这个就是 然后这题的话,,,我怎么记得之前考试好像有次考到了类似的题目昂,,,?反正我总觉着这方法似曾相识的样子,, ...
- 2018宁夏邀请赛 L Continuous Intervals(单调栈+线段树)
2018宁夏邀请赛 L Continuous Intervals(单调栈+线段树) 传送门:https://nanti.jisuanke.com/t/41296 题意: 给一个数列A 问在数列A中有多 ...
- 南昌网络赛 I. Max answer (单调栈 + 线段树)
https://nanti.jisuanke.com/t/38228 题意给你一个序列,对于每个连续子区间,有一个价值,等与这个区间和×区间最小值,求所有子区间的最大价值是多少. 分析:我们先用单调栈 ...
- The Preliminary Contest for ICPC China Nanchang National Invitational I.Max answer单调栈
题面 题意:一个5e5的数组,定义一个区间的值为 这个区间的和*这个区间的最小值,注意数组值有负数有正数,求所有区间中最大的值 题解:如果全是正数,那就是原题 POJ2796 单调栈做一下就ok 我们 ...
- 2019南昌网络赛-I(单调栈+线段树)
题目链接:https://nanti.jisuanke.com/t/38228 题意:定义一段区间的值为该区间的和×该区间的最小值,求给定数组的最大的区间值. 思路:比赛时还不会线段树,和队友在这题上 ...
- Codeforces - 1199D - Welfare State - 单调栈 / 线段树
https://codeforc.es/contest/1199/problem/D 其实后来想了一下貌似是个线段树的傻逼题. 单调栈是这样思考的,每次单点修改打上一个最终修改的时间戳.每次全体修改就 ...
随机推荐
- http协议和telnet指令讲解
http协议: 1.http:是网络传输协议:全称为:超文本传输协议: 关系:客户端和服务器的关系: 协议:就是一种规范: 常见的http和https两种,https是http的升级版 http协议: ...
- linux中.nfsxxxx引起的文件无法删除
一个linux系统中的某个文件夹无法删除,使用ls -al查看有1个.nfsxxxx的文件无法删除. 使用lsof +D /filepath/,查看到文件被一个进程一直占用. 再使用ps -aux | ...
- 记录1-更换mac pro内存,硬盘及恢复系统
我的mac pro是2012年初买的,4G/500G HDD在服役了六年多后速度堪比老牛,以前装的虚拟机压根不敢打开.这几天把内存更换为8G,硬盘升级为samsung的1T SSD,感觉像起死回生一样 ...
- (整理)REHL6.5_Yum安装Reids
1.yum添加epel源 yum install epel-release 默认安装,遇到“确定吗?”输入Y 2.yum安装Redis yum install redis 默认安装,遇到“确定吗?” ...
- 最近想学Json,请问大家有没有什么好的Json教程介绍一下?
最近想学json,请问大家有没有什么好的Json教程介绍一下? 最近学完java的框架了,想了解一下json,可是找不到相关视频,请大家有这方面的Json教程好资料就介绍下啦,最后有网址链接啦. {} ...
- udt的java实现
udt协议是什么? 我就不回答了,可以网上搜索,一直都是c++的,java的实现已经很久没有修改了 经过测试,java版本有些一问题,现在已经将其修复,已经上传到csdn 另外自己根据实际的应用,再次 ...
- Appium java环境搭建(Windows版)
注意:如果初次学习appium的话,则需要你做好准备因为安装过程并不简单 1.安装appium Appium 官方网站:http://appium.io/ 安装Appium之前需要先安装node.js ...
- cgi fast-cig php-fpm
cgi 通用网关接口,接受到动态请求,web服务器会根据这次请求的内容,然后会fork一个新进程来运行, 这个进程会把处理完的数据返回给web服务器,最后web服务器把内容发送给用户,刚才fork的进 ...
- Spring获取URL相关信息
获取请求的URL:request.getRequestURL().toString(); 获取上下文名称(项目名称):request.getContextPath()
- JAVA 8 函数式接口--Consumer
从JDK8开始java支持函数式编程,JDK也提供了几个常用的函数式接口,这篇主要介绍Consumer接口.文本介绍的顺序依次为: 源码介绍 使用实例 jdk内对Consumer的典型使用 扩展类介绍 ...