网络赛 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 其实后来想了一下貌似是个线段树的傻逼题. 单调栈是这样思考的,每次单点修改打上一个最终修改的时间戳.每次全体修改就 ...
随机推荐
- nginx1.14.0下载、安装、启动
nginx1.14.0下载及安装 wget http://nginx.org/download/nginx-1.14.0.tar.gztar -zxvf nginx-1.14.0.tar.gzcd n ...
- java Base36 算法
package com.github.linushp.wsblog.utils; import java.math.BigInteger; import java.nio.charset.Charse ...
- windows短路径转换成长路径
参考: https://blog.csdn.net/wxqian25/article/details/43951281 https://docs.microsoft.com/en-us/windows ...
- FTP上传文件,报错java.net.SocketException: Software caused connection abort: recv failed
FTP上传功能,使用之前写的代码,一直上传都没有问题,今天突然报这个错误: java.net.SocketException: Software caused connection abort: re ...
- 通过jedis远程访问redis服务器
一.jedis简介 类似于mysql数据库,一般开发都需要通过代码去访问redis服务器,对于主流的开发语言,redis提供了访问的客户端接口. https://redis.io/clients 而对 ...
- asp源码微信扫码授权登陆电脑版
网站接入微信扫码登录并获取用户基本信息(完美绕过微信开放平台)电脑版网站实现微信扫码登录,注册会员还要设密码太麻烦,会员也记不住密码,采用微信扫码登录网站更方便,会员无需设密码,用他的微信做为系统登录 ...
- Android学习笔记二之初始Activity
初识Activity,跟着教程学习敲了一遍这一章的内容,下面根据目录结构总结知识点: 一.活动是什么 上篇博文中我们提到了Android四大组件,其中有一个Activity,我们将之称为活动.活动是最 ...
- 推介一个学习JAVA的系列教程-狗鱼IT教程
介绍一个学JAVA的零基础学习JAVA的网站,推介一个学习JAVA的系列教程-狗鱼IT教程 下面是java的系教程: 1、[java教程]Java 教程 2、[java教程]Java 简介 3、[ja ...
- 部署Redis4.x单机版及配置RDB和AOF持久化
一.环境及软件 OS soft version CentOS 7.5 redis-4.0.12(目前是4.x最新) 二.下载及编译Redis [root@localhost ~]# wget h ...
- QT中QMainWindow、QWidget、QDialog
QT中QMainWindow.QWidget.QDialog 简述 在分享所有基础知识之前,很有必要在这里介绍下常用的窗口-QWidget.QDialog.QMainWindow. 熟悉Qt的同学都应 ...