原题链接
极值的和
内存限制: 256 Mb时间限制: 1000 ms
题目描述
给定一个序列 a1,a2,a3,…,an,请求出该序列每一个连续子序列的最大值,并计算这些最大值的和。
也就是求出
1≤i≤j≤n∑max{ai,ai+1,⋯,aj}
输入格式
- 第一行:单个整数 n
- 第二行:n 个整数表示 a1,a2,…,an
输出格式
- 单个整数:表示所有区间最大数的和
数据范围
- 对于 30% 的数据,1≤n≤500
- 对于 60% 的数据,01≤n≤50,000
- 对于 100% 的数据,1≤n≤500,000
- 0≤ai≤1,000,000
样例数据
输入:
2
6 8
输出:
22
说明:
6 + 8 + 8 = 22
输入:
3
4 5 6
输出:
32
解析:
对于60%的数据,可以使用O(n^2)的算法即,递推出所有组合的最大值,详见代码:
#include<bits/stdc++.h>
using namespace std;
int n;
int a[50005];
long long ans = 0;
int main() {
scanf("%d", &n);
if (n <= 50000) {
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
ans += a[i];
}
for (int i = n - 1; i >= 1; i--) {
for (int j = 1; j <= i; j++) {
a[j] = max(a[j], a[j + 1]);
ans += a[j];
}
}
printf("%ld", ans);
}
return 0;
}
对于在(l,r)区间的最大值x,若x的位置是t,我们可以分析出,其是(t-l)*(r-t)个组合的最大值,我们只要找到每一个数的最大值区间就可以了。我们可以采用单调队列来找到每一个数作为最大值的(l,r)区间。详见代码:
#include<bits/stdc++.h>
using namespace std;
int n;
long long ans=0;
struct node{
int id;
int v;
};
stack <node> s;//单调栈,保证栈内元素不递增
int main() {
scanf("%d", &n);
for (int i=1;i<=n+1;i++){
node x;
if (i==n+1){//设置尾部哨兵
x.v=1e9;
}else{
scanf("%d",&x.v);
}
x.id=i;
//如果当前元素大于栈顶,则栈顶元素的右边界为当前元素位置,左边界为次栈顶元素位置
//我们可以计算其贡献值
while(!s.empty()&&s.top().v<x.v){
node now=s.top();//取栈顶元素
s.pop();//弹出栈顶元素
int p,q=i;//pq为左右边界
if (!s.empty()){//如果栈内非空,左边界为栈顶元素ID
p=s.top().id;
}else {//否则左边界为0
p=0;
}
//计算贡献
ans+=(long long)(q-now.id)*(now.id-p)*now.v;
}
s.push(x);//当前元素入栈
}
cout<<ans;
return 0;
}