子串的最大差(单调栈)
定义序列的最大差为序列中最大数与最小数的差。比如 $ (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 }

浙公网安备 33010602011771号