思路
问题可以转化为 求每个区间(区间长度大于1的区间)的最大值 与 最小值,最大值之和 减去 最小值之和 就是答案
注:下面所提区间,默认 区间长度大于 1
如何求 最大值之和 与 最小值之和
1:枚举每个区间,时间复杂度 O(n^2) ==> 超时 2:枚举每个值,假设该值为区间最大值 此时 num_1 * maxm_1 + num_2 * maxm_2 + ... + num_n * maxm_n = 最大值之和 num_i 表示 maxm_i 这个值 可以在 num_i 个区间里作为最大值 求最小值之和 同理
求 num_i :
不妨令 a[i] 为最大值,我们求出 a[i] 的左右边界 l[i]、r[i]:左边/右边第一个比a[i]大的值的位置 则 num_i = (r[i] - l[i] - 2) + (i - l[i] - 1) * (r[i] - i - 1) r[i] - l[i] - 2 :a[i] 为区间端点时的情况 r[i] - i - 1 : a[i] 为区间内部点时的情况
求 l[i] 与 r[i] :
由于 l[i]、r[i] 的性质:左边/右边第一个比a[i]大的值的位置, 我们可以使用单调栈来求l[i]、r[i] 从左到右 跑一遍求出 l[i],从右到左跑一遍求出 r[i]
关于代码中两处位置 一个加等号 一个不加的解释
若存在 a[i] = a[i+1] ,比如 1、5、5 :a[2] = a[3] 对于 样例 1、5、5,我们应该得到的答案是 8,但两处都加等号实际得到的却是 13 这是因为此时: 对于区间 [1,3] 有两个最大值 5,一个最小值 1 对于区间 [2,3] 有两个最大值 5,两个最小值 5 对于区间 [1,2] 有一个最大值 5,一个最小值 1 最大值之和为:5*2 + 5*2 + 5 = 25 最小值之和为:1 + 5*2 + 1 = 12 结果:25 - 12 = 13 != 8(预期结果) 所以我们需要一个加等于号 一个不加,避免上述现象, 可验证 对于栗子:1、5、5,一个加等于号 一个不加 得到的结果是 8,与预期一致
Code
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e6+10; int l[N],r[N]; int stk[N],a[N]; int n,hh; ll solve(){ hh=0; stk[++hh]=0; a[0]=1e6; for(int i=1;i<=n;i++) { while(hh&&a[i]>=a[stk[hh]]) hh--; // 一个加等于号 l[i]=stk[hh]; stk[++hh]=i; } hh=0; stk[++hh]=n+1; a[n+1]=1e6; for(int i=n;i>=1;i--) { while(hh&&a[i]>a[stk[hh]]) hh--; // 一个不加等于号 r[i]=stk[hh]; stk[++hh]=i; } ll res=0; for(int i=1;i<=n;i++){ res+=(ll)a[i]*(r[i]-l[i]-2); //避免爆掉 int res+=(ll)a[i]*(i-l[i]-1)*(r[i]-i-1); } return res; } int main(){ int T; cin>>T; while(T--){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; ll res=solve(); for(int i=1;i<=n;i++) a[i]=-a[i]; // a[i] 变为相反数后 求出的最大值 是 原先的最小值 cout<<res+solve()<<endl; } return 0; }