题目意思:
给你一个序列,让你求其中一个子序列使得这个子序列和乘以这个子序列中最小值后最大。
做法:
1、 单调栈的典型题,可以吧每个 emotion[i] 看做是一个矩形,宽度为1,高度为 emotion[i]
2、 题目转化为,以某点为中心(假设这点emotion 是最小的),往左和往右延伸,只要大于该中心的,就加入。
题目要求,所有的矩形的面积总和 * 该点的高度emotion[i] 。
假设这点为坐标i,往左能延伸的下标为 Left[i], 往右能延伸的下标为 Right[i]
ans = emotion[i] * (Left[i] 到 Right[i]之间的所有矩形的面积总和)
3、 区间的个元素的和,使用前缀和得到
#include <cstdio>
#include <cstring>
#include <iostream>
#include <stack>
#include <algorithm>
typedef long long LL;
using namespace std;
const int MaxN = 100010;
int n;
LL emotion[MaxN];
int Left[MaxN];
int Right[MaxN];
LL pre_sum[MaxN]; //前缀和
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
{
scanf("%lld", &emotion[i]);
pre_sum[i] = pre_sum[i - 1] + emotion[i];
}
stack<int> s;
for(int i = n; i >= 1; --i)
{
while(!s.empty() && emotion[i] <= emotion[s.top()])
{
s.pop();
}
if(s.empty())
{
Right[i] = n;
}else{
Right[i] = s.top() - 1;
}
s.push(i);
}
stack<int> s2;
for(int i = 1; i <= n; ++i)
{
while(!s2.empty() && emotion[i] <= emotion[s2.top()])
{
s2.pop();
}
if(s2.empty())
{
Left[i] = 1;
}else{
Left[i] = s2.top() + 1;
}
s2.push(i);
}
LL maxAns = -1, tmp;
int ansL, ansR;
for(int i = 1; i <= n; ++i)
{
int low = Left[i], high = Right[i];
tmp = (pre_sum[high] - pre_sum[low - 1]) * emotion[i];
if(maxAns < tmp)
{
maxAns = tmp;
ansL = low, ansR = high;
}
}
printf("%lld\n", maxAns);
printf("%d %d\n", ansL, ansR);
return 0;
}
/*
6
3 1 6 4 5 2
*/
/*
60
3 5
*/