problem

【代码源 Div1#103】子串的最大差 Codeforces - 817D,力扣2104,1900分_算法

【代码源 Div1#103】子串的最大差 Codeforces - 817D,力扣2104,1900分_codeforces_02


视频讲解链接:https://www.bilibili.com/video/BV1Du411X7Nk

solution

  • 可以直接推导原答案ans = 【代码源 Div1#103】子串的最大差 Codeforces - 817D,力扣2104,1900分_i++_03,即对于每一段来说的最大值和最小值。
  • 或者从贡献的角度考虑:对于一个数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;
}