题目链接: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 单调栈+线段树的更多相关文章

  1. 2019ICPC南昌邀请赛网络赛 I. Max answer (单调栈+线段树/笛卡尔树)

    题目链接 题意:求一个序列的最大的(区间最小值*区间和) 线段树做法:用单调栈求出每个数两边比它大的左右边界,然后用线段树求出每段区间的和sum.最小前缀lsum.最小后缀rsum,枚举每个数a[i] ...

  2. 南昌邀请赛I.Max answer 单调栈+线段树

    题目链接:https://nanti.jisuanke.com/t/38228 Alice has a magic array. She suggests that the value of a in ...

  3. The Preliminary Contest for ICPC China Nanchang National Invitational I. Max answer (单调栈+线段树)

    题目链接:https://nanti.jisuanke.com/t/38228 题目大意:一个区间的值等于该区间的和乘以区间的最小值.给出一个含有n个数的序列(序列的值有正有负),找到该序列的区间最大 ...

  4. 洛谷P4198 楼房重建 单调栈+线段树

    正解:单调栈+线段树 解题报告: 传送门! 首先考虑不修改的话就是个单调栈板子题昂,这个就是 然后这题的话,,,我怎么记得之前考试好像有次考到了类似的题目昂,,,?反正我总觉着这方法似曾相识的样子,, ...

  5. 2018宁夏邀请赛 L Continuous Intervals(单调栈+线段树)

    2018宁夏邀请赛 L Continuous Intervals(单调栈+线段树) 传送门:https://nanti.jisuanke.com/t/41296 题意: 给一个数列A 问在数列A中有多 ...

  6. 南昌网络赛 I. Max answer (单调栈 + 线段树)

    https://nanti.jisuanke.com/t/38228 题意给你一个序列,对于每个连续子区间,有一个价值,等与这个区间和×区间最小值,求所有子区间的最大价值是多少. 分析:我们先用单调栈 ...

  7. The Preliminary Contest for ICPC China Nanchang National Invitational I.Max answer单调栈

    题面 题意:一个5e5的数组,定义一个区间的值为 这个区间的和*这个区间的最小值,注意数组值有负数有正数,求所有区间中最大的值 题解:如果全是正数,那就是原题 POJ2796 单调栈做一下就ok 我们 ...

  8. 2019南昌网络赛-I(单调栈+线段树)

    题目链接:https://nanti.jisuanke.com/t/38228 题意:定义一段区间的值为该区间的和×该区间的最小值,求给定数组的最大的区间值. 思路:比赛时还不会线段树,和队友在这题上 ...

  9. Codeforces - 1199D - Welfare State - 单调栈 / 线段树

    https://codeforc.es/contest/1199/problem/D 其实后来想了一下貌似是个线段树的傻逼题. 单调栈是这样思考的,每次单点修改打上一个最终修改的时间戳.每次全体修改就 ...

随机推荐

  1. 图解Tomcat

  2. DynamicEnumUtil 动态添加枚举类的枚举值

    import java.lang.reflect.AccessibleObject; import java.lang.reflect.Array; import java.lang.reflect. ...

  3. Listen and Write 18th Feb 2019

    Weighted blanket has becomes very popular in many homes. they claim it can provide better sleep and ...

  4. DOM事件类

    1.DOM中的事件级别 DOM0: element.onclick = function(){} DOM1: 没有与事件相关的设计 DOM2: element.addEventListener('cl ...

  5. 如何让SQLServer的 itemNum 字段 按照数字大小顺序排序

    我的 itemNum 从1到20,可是超过了SQLServer的默认排序这样的1101112...19234567如何才能让排序成为这样1234567891011.. . 解决办法:因为 itemNu ...

  6. 转: 解决Setting property 'source' to 'org.eclipse.jst.jee.server的问题

    我发现这个问题上网搜索 ,找到的地址为:http://blog.csdn.net/z69183787/article/details/19911935 .但是他的标题上也有一个"转" ...

  7. JS面试Q&A(续2): Rest parameter,Arrow function 等

    rest parameter 和 Destructuring assignment. function fun1(...theArgs) { console.log(theArgs.length);} ...

  8. C博客01——分支,顺序结构

    C博客01--分支,顺序结构 1. 本章学习总结 1.1 思维导图 请以思维导图总结本周的学习内容. 1.2 本章学习体会及代码量体会 1.2.1 学习体会 对于C语言课程的理解,我有点吃力,不是说老 ...

  9. Z 字形变换

    将一个给定字符串根据给定的行数,以从上往下.从左到右进行 Z 字形排列. 比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下: L C I R E T ...

  10. 学习excel的使用技巧复制一列文本成新列去重

    学习excel的使用技巧复制一列文本成新列去重 其实比较简单的技巧  知道了就会  不知道就比较麻烦 直接复制到一列 找到 数据选项 删除重复项