連續子序列的權值
題目
知識點:單調棧
我們定義連續序列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;
}