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 <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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章