南昌网络赛 I. Max answer 单调栈

本文介绍了一种解决特定算法问题的方法,该问题要求找出一个序列中所有连续子区间的最大价值,价值定义为子区间和与区间最小值的乘积。文章详细解释了如何使用单调栈计算每个元素左侧和右侧最近的小于它的元素,并通过贪心策略确定每个位置的最大区间和,最终找到全局最大价值。

Max answer

题目链接

https://nanti.jisuanke.com/t/38228

Describe

Alice has a magic array. She suggests that the value of a interval is equal to the sum of the values in the interval, multiplied by the smallest value in the interval.

Now she is planning to find the max value of the intervals in her array. Can you help her?

Input

First line contains an integer n(1≤n≤5*10^5)

Second line contains n integers represent the array a(−10^5≤ai≤10^5)

Output

One line contains an integer represent the answer of the array.

样例输入

5

1 2 3 4 5

样例输出

36

 

题意
给你一个序列,对于每个连续子区间,有一个价值,等与这个区间和×区间最小值,
求所有子区间的最大价值是多少。

 

题解

a[i]表示第i位的权值,L[i]表示第i个数左边第一个比他小的数,R[i]表示右边第一个比它小的数。

我们可以用单调站求出来,不会可以先做洛谷的完美序列, https://www.luogu.org/problemnew/show/P2659

然后对于每一位i,如果a[i]为正数,我们可以求包含i的区间和最大的子区间,然后再乘上这个值,当然不能越过L[i]和R[i]。

我们怎么求呢?

我们先定义sum(i,j)为i到j的区间和,再定义lmax[i]为以i 为右端点的使得区间和和最大的左端点位置,(即求一个l<=i,使得sum(l,i)最大)。

那么,lmax[i]可以通过lmax[i-1]更新,这个就是一个贪心吧,分两种情况:

1.sum(lmax[i-1],i-1)<0, 那么lmax[i]=i.

2.sum(lmax[i-1],i-1)>0,lmax[i+1]=lmax[i].

相似的我们可以求出lmax[i],lmin[i],rmax[i]。对于i ,若a[i]>0,其最大贡献就是sum(max(L[i]+1,lmax[i]),min(R[i]-1,rmax[i])*a[i]。负数类似。

这样题目就做完啦,时间复杂度O(n)

 

代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 3000050
 4 #define INF 0x7f7f7f7f
 5 #define ll long long
 6 template<typename T>void read(T&x)
 7 ll n,a[N],s[N],sk[N],L[N],R[N];
 8 ll Rmin[N],Lmin[N],Rmax[N],Lmax[N],ans,top;
 9 int main()
10 {
11 #ifndef ONLINE_JUDGE
12   freopen("aa.in","r",stdin);
13 #endif
14   ios::sync_with_stdio(false);
15   cin>>n;
16   for(ll i=1;i<=n;i++)cin>>a[i],s[i]=a[i]+s[i-1];
17   ans=a[1]*a[1];
18   sk[top=1]=0;
19   a[0]=-INF-1;
20   a[n+1]=-INF;
21   for(ll i=1;i<=n+1;i++)
22     {
23       while(a[i]<a[sk[top]])
24     R[sk[top--]]=i;
25       L[i]=sk[top];
26       sk[++top]=i;
27     }
28   Lmin[1]=1;
29   Lmax[1]=1;
30   Rmin[n]=n;
31   Rmax[n]=n;
32   for(ll i=2;i<=n;i++)
33     {
34       if (s[i-1]-s[Lmin[i-1]-1]>0)Lmin[i]=i;
35       else Lmin[i]=Lmin[i-1];
36       if (s[i-1]-s[Lmax[i-1]-1]<0)Lmax[i]=i;
37       else Lmax[i]=Lmax[i-1];
38     }
39   for(ll i=n-1;i>=1;i--)
40     {
41       if (s[Rmin[i+1]]-s[i]>0)Rmin[i]=i;
42       else Rmin[i]=Rmin[i+1];
43       if (s[Rmax[i+1]]-s[i]<0)Rmax[i]=i;
44       else Rmax[i]=Rmax[i+1];
45     }
46   ll l,r;
47   for(ll i=1;i<=n;i++)
48     {
49       if (a[i]<0)
50     {
51       l=max(L[i]+1,Lmin[i]);
52       r=min(R[i]-1,Rmin[i]);
53     }
54       else
55     {
56       l=max(L[i]+1,Lmax[i]);
57       r=min(R[i]-1,Rmax[i]);
58     }
59       ans=max((s[r]-s[l-1])*a[i],ans);
60     }
61   cout<<ans;
62 }
View Code

 

 

 

转载于:https://www.cnblogs.com/mmmqqdd/p/10743511.html

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值