problem
视频讲解链接:https://www.bilibili.com/video/BV1Du411X7Nk
solution
- 可以直接推导原答案ans =
,即对于每一段来说的最大值和最小值。
- 或者从贡献的角度考虑:对于一个数Ai来讲,如果其有贡献的价值,要么是-Ai作为最小值,要么是+Ai作为最大值。
那么Ans=ΣAi*maxn-Ai*minn,这里maxn表示Ai作为最大值出现的次数,minn表示Ai作为最小值出现的次数。 - 考虑如何计算这个maxn和minn
我们设定L【i】表示Ai作为最大值时,左边可以延展到的位子,R【i】表示Ai作为最大值时,右边可以延展到的位子。
那么对于Ai来讲,其maxn=(i-L【i】+1)-(R【i】-i+1);
L【i】以及R【i】都可以O(n)维护。
那么求minn的过程同理相反。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e6+10;
LL a[maxn], l[maxn], r[maxn];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n; cin>>n;
for(int i = 1; i <= n; i++)cin>>a[i];
LL ans = 0;
//maxn
for(LL i = 1; i <= n; i++)l[i]=r[i]=i;
for(int i = 2; i <= n; i++){
int now = i;
while(now>1&&a[i]>=a[now-1])now=l[now-1];
l[i] = now;
}
for(int i = n-1; i >= 1; i--){
int now = i;
while(now<n&&a[i]>a[now+1])now=r[now+1];
r[i] = now;
}
for(LL i = 1; i <= n; i++){
ans += a[i]*(i-l[i]+1)*(r[i]-i+1);
}
//minn
for(LL i = 1; i <= n; i++)l[i]=r[i]=i;
for(int i = 2; i <= n; i++){
int now = i;
while(now>1&&a[i]<=a[now-1])now=l[now-1];
l[i] = now;
}
for(int i = n-1; i >= 1; i--){
int now = i;
while(now<n&&a[i]<a[now+1])now=r[now+1];
r[i] = now;
}
for(LL i = 1; i <= n; i++){
ans -= a[i]*(i-l[i]+1)*(r[i]-i+1);
}
cout<<ans<<"\n";
return 0;
}