1. 程式人生 > 其它 >子串的最大差(單調棧)

子串的最大差(單調棧)

定義序列的最大差為序列中最大數與最小數的差。比如 $ (3,1,4,5,6) $ 的最大差為 $ 6 - 1 = 5 $ , $ (2,2) $ 的最大差為 $ 2 - 2 = 0 $ 。

定義一個序列的子串為該序列中連續的一段序列。

給定一個長度為 $n$ 的陣列 $a_1,a_2,\dots ,a_n$,請求出這個序列的所有子串的最大差之和。

輸入格式

第一行一個數字 $n$。

接下來一行 $n$ 個整數 $a_1, a_2, \dots, a_n$。

輸出格式

一個數,表示答案。

樣例輸入

3
1 2 3

樣例輸出

4

資料規模

所有資料保證 $1\leq n\leq 500000, 0 \leq a_i \leq 10^8$。

 1 #include <iostream>
 2 #include <vector>
 3 #include <map>
 4 #include <set>
 5 #include <cmath>
 6 #include <queue>
 7 #include <stack>
 8 #include <string>
 9 #include <vector>
10 #include <cstring>
11 #include <iterator>
12 #include <algorithm>
13
using ll = long long; 14 using namespace std; 15 using PII = pair<int, int>; 16 const int MAXN = 5e5 + 7, mod = 998244353; 17 #define rep(i, begin, end) for (__typeof(end) i = (begin) - ((begin) > (end)); i != (end) - ((begin) > (end)); i += 1 - 2 * ((begin) > (end))) 18 #define error(args...) { string _s = #args; replace(_s.begin(), _s.end(), ',', ' '); stringstream _ss(_s); istream_iterator<string> _it(_ss); err(_it, args); } 19
20 void err(istream_iterator<string> it) {} 21 template<typename T, typename... Args> 22 void err(istream_iterator<string> it, T a, Args... args) { 23 cerr << *it << " = " << a << endl; 24 err(++it, args...); 25 } 26 27 vector<ll> a, b; 28 int n, nums[MAXN]; 29 vector<ll> getCnt(int *nums, bool ismin){ 30 stack<int> s; 31 vector<ll> a(n + 1), b(n + 1), ans(n + 1); 32 for(int i = 1; i <= n; ++ i){ 33 //如果isMin為真, 找到nums[i]左邊第一個小於nums[i]的位置 34 //否則找到nums[i]左邊第一個大於nums[i]的位置 35 while(!s.empty() && (ismin? nums[s.top()] >= nums[i] : nums[s.top()] <= nums[i])){ 36 s.pop(); 37 } 38 a[i] = s.empty()? 0 : s.top(); 39 s.push(i); 40 } 41 42 while(!s.empty()) 43 s.pop(); 44 for(int i = n; i >= 1; -- i){ 45 //如果isMin為真, 找到nums[i]右邊第一個小於nums[i]的位置 46 //否則找到nums[i]右邊第一個大於nums[i]的位置 47 while(!s.empty() && (ismin? nums[s.top()] > nums[i] : nums[s.top()] < nums[i])){ 48 s.pop(); 49 } 50 b[i] = s.empty()? n + 1 : s.top(); 51 s.push(i); 52 } 53 for(int i = 1; i <= n; ++ i){ 54 //nums[i]作為區間最值, 在它左邊有(i - a[i])個區間端點可選 55 //在它右邊有(b[i] - i)個區間端點可選 56 //所以它作為區間最值的次數為(i - a[i]) * (b[i] - i) 57 ans[i] = (i - a[i]) * 1ll * (b[i] - i); 58 } 59 return ans; 60 } 61 void solve(){ 62 ll ans = 0; 63 cin >> n; 64 for(int i = 1; i <= n; ++ i){ 65 cin >> nums[i]; 66 } 67 //a[i] 為 nums[i] 作為區間最小值的次數;b[i] 為 nums[i] 作為區間最大值的次數 68 a = getCnt(nums, true); 69 b = getCnt(nums, false); 70 71 for(int i = 1; i <= n; ++ i){ 72 //nums[i]作為區間max出現b[i]次, 作為區間min出現a[i]次 73 //那麼對最終答案的貢獻就是(b[i] - a[i]) * nums[i]; 74 ans += (b[i] - a[i]) * nums[i]; 75 } 76 77 cout << ans << '\n'; 78 } 79 int main(){ 80 81 int T; 82 ll x, y; 83 ios::sync_with_stdio(false); 84 cin.tie(nullptr); 85 cout.tie(nullptr); 86 87 solve(); 88 89 return 0; 90 }
View Code