洛谷 P6503 [COCI 2010/2011 #3] DIFERENCIJA 题解

题目链接

洛谷 P6503 [COCI 2010/2011 #3] DIFERENCIJA

题目大意

给定 \(a_{[1..n]}\),求下式值。

\[\sum_{i=1}^{n}\sum_{j=i}^{n}{(\max_{i\le k\le j}{a_k}-\min_{i\le k\le j}{a_k})} \]

思路分析 - 1

采用动态规划。定义 \(f_i,g_i\) 分别表示以 \(i\) 结尾的所有子序列最大值之和与最小值之和,则题目所求即转换为 \(\sum_{i=1}^{n}{f_i-g_i}\)

再定义 \(p_i\) 表示第一个满足 \(p<i\)\(a_{p}>a_i\)\(p\) 值,特别地,若 \(p\) 值不存在,则 \(p_i=0\)。根据定义,\(p_i\) 可以用一个单调栈维护。

再来看转移:

  • 根据定义,序列 \(a_{[j..i]}\)\(a_{[j..p_i]}\) 的最大值同为 \(a_{p_i}\) \((1\le j\le p_i)\)\(f_i\) 可以直接由 \(f_{p_i}\) 转移得到;
  • 而序列 \(a_{[j..i]}\) 的最大值都为 \(a_i\) \((p_i<j\le i)\),所以 \(f_i\) 应加上 \(i-p_i\)\(a_i\)

综上,状态转移方程为:

\[f_i=f_{p_i}+a_i\times (i-p_i) \]

对于 \(g_i\) 来说,同理,只需把定义中大于号改为小于即可。总时间复杂度 \(O(n)\)

代码精讲 - 1

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=3e5+10;
int n;
int a[N];
ll f[N],g[N];

int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;++i) scanf("%d",a+i);
    stack<int> s,t;
    for (int i=1;i<=n;++i){
        while (!s.empty() && a[i]>=a[s.top()]) s.pop();
        if (!s.empty()) f[i]=f[s.top()]+(i-s.top())*1ll*a[i]; // s.top() 即为 p[i]
        else f[i]=i*1ll*a[i]; // 注意队列非空的特判
        s.push(i);
        while (!t.empty() && a[i]<=a[t.top()]) t.pop();
        if (!t.empty()) g[i]=g[t.top()]+(i-t.top())*1ll*a[i];
        else g[i]=i*1ll*a[i];
        t.push(i);
    }
    ll ans=0;
    for (int i=1;i<=n;++i) ans+=f[i]-g[i];
    printf("%lld",ans);
    return 0;
}

思路分析 - 2

首先将式子拆开。

\[\sum_{i=1}^{n}\sum_{j=i}^{n}{(\max_{i\le k\le j}{a_k}-\min_{i\le k\le j}{a_k})}=\sum_{i=1}^{n}\sum_{j=i}^{n}{\max_{i\le k\le j}{a_k}}-\sum_{i=1}^{n}\sum_{j=i}^{n}{\min_{i\le k\le j}{a_k}} \]

我们只需考虑最大值部分,因为另一部分同理可得。

对于前一部分,我们考虑每个 \(a_i\) 会成为多少次最大值。定义 \(l_i\) 表示满足 \(a_{l_i}\le a_i\) 的最大下标,\(r_i\) 表示满足 \(a_{r_i}\ge a_i\) 的最小下标。则由定义 \(a_i\)\(a_{[l_i,r_i]}\) 序列中的最大值。所以共有 \((i-l_i)\times(r_i-i)\) 个序列以 \(a_i\) 为最大值。

同时对于 \(l_i\)\(r_i\) 可以利用单调栈预处理,总时间复杂度 \(O(n)\)

代码精讲 - 2

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=3e5+10;
int n;
int a[N],l1[N],r1[N],l2[N],r2[N];

int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;++i) scanf("%d",a+i);
    stack<int> s,t,u,v;
    for (int i=1;i<=n;++i){ // 维护
        while (!s.empty() && a[i]>=a[s.top()]) s.pop();
        l1[i]=s.empty()?0:s.top();
        s.push(i);
    }
    for (int i=n;i>=1;--i){
        while (!t.empty() && a[i]>a[t.top()]) t.pop();
        r1[i]=t.empty()?n+1:t.top();
        t.push(i);
    }
    for (int i=1;i<=n;++i){
        while (!u.empty() && a[i]<=a[u.top()]) u.pop();
        l2[i]=u.empty()?0:u.top();
        u.push(i);
    }
    for (int i=n;i>=1;--i){
        while (!v.empty() && a[i]<a[v.top()]) v.pop();
        r2[i]=v.empty()?n+1:v.top();
        v.push(i);
    }
    ll ans=0;
    for (int i=1;i<=n;++i) // 统计
        ans+=a[i]*((i-l1[i])*1ll*(r1[i]-i)-(i-l2[i])*1ll*(r2[i]-i));
    printf("%lld",ans);
    return 0;
}
posted @ 2025-11-30 16:48  CodingJuRuo  阅读(1)  评论(0)    收藏  举报