Imbalanced Array

题目描述

You are given an array $a$ consisting of $n$ elements. The imbalance value of some subsegment of this array is the difference between the maximum and minimum element from this segment. The imbalance value of the array is the sum of imbalance values of all subsegments of this array.
For example, the imbalance value of array $[1, 4, 1]$ is $9$, because there are $6$ different subsegments of this array:

  • $[1]$ (from index $1$ to index $1$), imbalance value is $0$;
  • $[1, 4]$ (from index $1$ to index $2$), imbalance value is $3$;
  • $[1, 4, 1]$ (from index $1$ to index $3$), imbalance value is $3$;
  • $[4]$ (from index $2$ to index $2$), imbalance value is $0$;
  • $[4, 1]$ (from index $2$ to index $3$), imbalance value is $3$;
  • $[1]$ (from index $3$ to index $3$), imbalance value is $0$.

You have to determine the imbalance value of the array $a$.

题意概述

给定一个长度为$n$的$a$数组,求
$$\sum_{1 \le i \le j \le n} (max_{i, j}-min_{i, j})$$
其中$max_{i, j}$表示$a_i, a_{i+1}, a_{i+2}, \ldots, a_j$中的最大值,$min_{i, j}$表示$a_i, a_{i+1}, a_{i+2}, \ldots, a_j$中的最小值。
数据范围:$1 \le n \le 10^6, \; 1 \le a_i \le 10^6$。

算法分析1

考虑对于第$i$个数$a_i$,设它作为最大值出现的次数为$mx_i$,作为最小值出现的次数为$mn_i$。那么$ans=\sum_{i=1}^n(mx_i-mn_i)a_i$。接下来的问题就变成了求$mx_i, mn_i$。
对于区间$[l, r]$,我们找出其中的最大(小)值,设为$a_j$,那么$a_j$在$[l, r]$上的$mx_i$($mn_i$)就等于$(j-l+1)(r-j+1)$。可以发现,求解区间$[l, j-1]$和$[j+1, r]$是这个问题的子问题,而且规模更小,可以递归求解。只需在开始时先将数组从大到小(从小到大)排序,令$l=1, r=n$,即可解决原问题。
在求解时可以倒过来思考,即从小到大(从大到小)枚举,并将连续的区间合并,用并查集维护。理论时间复杂度为$O(n\log n)$。

代码1

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
struct number {
    long long num, id, rec;
} a[1000001];
bool cmp(number a, number b) {return a.num < b.num;}
long long n, fa[1000002], s[1000002], ans;
int get_fa(long long t) {
    return t == fa[t] || fa[t] == 0 ? fa[t] : fa[t] = get_fa(fa[t]);
}
void process(int t) {
    int i, j;
    if (t == 1) i = 1, j = n + 1;
    else i = n, j = 0;
    for (; i != j; i += t) {
        fa[a[i].id] = a[i].id;
        s[a[i].id] = 1;
        long long p = get_fa(a[i].id - 1), q = get_fa(a[i].id + 1);
        a[i].rec = (s[p] + 1) * (s[q] + 1);
        if (s[p]) {
            s[p] += s[get_fa(a[i].id)];
            fa[get_fa(a[i].id)] = p;
        }
        if (s[q]) {
            s[q] += s[get_fa(a[i].id)];
            fa[get_fa(a[i].id)] = q;
        }
    }
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i].num;
        a[i].id = i;
    }
    sort(a + 1, a + n + 1, cmp);
    process(1);
    for (int i = 1; i <= n; ++i) {
        ans += a[i].num * a[i].rec;
    }
    memset(fa, 0, sizeof(fa));
    memset(s, 0, sizeof(s));
    process(-1);
    for (int i = 1; i <= n; ++i) {
        ans -= a[i].num * a[i].rec;
    }
    cout << ans << endl;
    return 0;
}

算法分析2

设$l_i, r_i$分别表示$a_i$作为区间$[l, r]$中的最大(小)值时,$l$的最小值和$r$的最大值。那么对于$a_i$来讲,$mx_i(mn_i)=(i-l_i+1)(r_i-i+1)$。$l_i, r_i$均可以$O(n)$维护(DP或单调栈),从而避免了排序。
注意到有多个数相等时会有重复计算的区间。根据容斥原理,在判断时应一边取等号一边不取等号。

代码2

#include <iostream>
using namespace std;
long long n, a[1000001], l[1000001], r[1000001], ans;
void process(int t) {
    for (int 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] * t >= a[now - 1] * t) now = l[now - 1];
        l[i] = now;
    }
    for (int i = n - 1; i; --i) {
        int now = i;
        while (now < n && a[i] * t > a[now + 1] * t) now = r[now + 1];
        r[i] = now;
    }
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    process(1);
    for (int i = 1; i <= n; ++i) {
        ans += a[i] * (i - l[i] + 1) * (r[i] - i + 1);
    }
    process(-1);
    for (int i = 1; i <= n; ++i) {
        ans -= a[i] * (i - l[i] + 1) * (r[i] - i + 1);
    }
    cout << ans << endl;
    return 0;
}

RegMs If

418 I'm a teapot

Leave a Reply

Your email address will not be published. Required fields are marked *