2019E0_C连续子序列的权值

连续子序列的权值

题目

知识点:单调栈

我们定义连续序列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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
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;
}

你可能感兴趣的:(算法,算法,单调栈)