连续子序列的权值
题目
知识点:单调栈
我们定义连续序列a[p],a[p+1]…a[q]的权值为max(a[p],a[p+1]…a[q])- min(a[p],a[p+1]…a[q]),给定一个由N个整数组成的序列,请求出所有连续子序列的权值和。
输入
第1行:1个数N,表示数组的长度。(1<=N<=50000)
第2−N+1行:每行1个数,表示数组中的元素(1<=A[p]<=50000)
输出
输出所有连续子序列的权值和。
输入样例
5
1
2
3
4
5
输出样例
20
思路
我们定义连续序列a[p],a[p+1]…a[q]的权值为max(a[p],a[p+1]…a[q)- min(a[p],a[p+1]…a[q]),给定一个由N个整数组成的序列,请求出所有连续子序列的权值和。
首先题目的意思可以转化为求所有的子数组最大值之 和减去所有的子数组最小值之和。
那么我们可以通过两次单调栈求得以每个 a[i]作为最大以最小的左右两端能到达的端点。
然后以每个 a[i]作为贡献的区间的值是多少呢?因为当 a[i]作为贡献时子数组区间必过 a[i],那么我们只需要在 a[i] 作为贡献的左区间和右区间任选 2 个组成区间即为 a[i]的贡 献,所以是 a[i](i-l[i]+1)(r[i]-i+1)。
代码
#include <cstdlib>
#include <algorithm>
#include <ctime>
#include <iostream>
#include <map>
#include <random>
#include <queue>
#include <cstring>
#include <set>
#include <stack>
using namespace std;
typedef long long ll;
const int ms = 50050;
int a[ms], l[ms];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
cin >> n;
for (int i = 0; i < n; ++i)
{
cin >> a[i];
}
ll res = 0;
stack<int> s;
for (int i = 0; i < n; ++i)
{
while (!s.empty() && a[s.top()] < a[i])
{
res += 1ll * (i - s.top())*(s.top() - l[s.top()]) * a[s.top()];
s.pop();
}
if (s.empty()) l[i] = -1;
else l[i] = s.top();
s.push(i);
}
ll r = n;
while (!s.empty())
{
res += 1ll * (r - s.top())*(s.top() - l[s.top()])* a[s.top()];;
s.pop();
}
for (int i = 0; i < n; ++i)
{
while (!s.empty() && a[s.top()] > a[i])
{
res -= 1ll * (i - s.top())*(s.top() - l[s.top()])* a[s.top()];;
s.pop();
}
if (s.empty()) l[i] = -1;
else l[i] = s.top();
s.push(i);
}
r = n;
while (!s.empty())
{
res -= 1ll * (r - s.top())*(s.top() - l[s.top()])* a[s.top()];;
s.pop();
}
cout << res;
return 0;
}
来源:https://blog.csdn.net/weixin_44024733/article/details/102752415