NC20806 区区区间间间

题目

题目描述

给出长度为n的序列a,其中第i个元素为 \(a_i\),定义区间(l,r)的价值为

\(v_{l,r} = max(a_i - a_j | l \leqslant i,j\leqslant r)\)

请你计算出 \(\sum_{l = 1}^n \sum_{r = l + 1}^n v_{l,r}\)

输入描述

第一行输入数据组数T
对于每组数据,第一行为一个整数n,表示序列长度
接下来一行有n个数,表示序列内的元素

输出描述

对于每组数据,输出一个整数表示答案

示例1

输入

3
3
4 2 3
5
1 8 4 3 9
20
2 8 15 1 10 5 19 19 3 5 6 6 2 8 2 12 16 3 8 17 

输出

5
57
2712

说明

对于一组测试数据的解释:
区间[1, 2]的贡献为:4 - 2 = 2
区间[1, 3]的贡献为:4 - 2 = 2
区间[2, 3]的贡献为:3 - 2 = 1
2 + 1 + 2 = 5.

备注

\(T \leqslant 20,n\leqslant 10^5, 0 \leqslant a_i \leqslant 10^5\)
不保证数据随机生成!

题解

知识点:单调栈。

\(\sum_{l = 1}^n \sum_{r = l + 1}^n v_{l,r} = \sum_{l = 1}^n \sum_{r = l + 1}^n (max_{l,r} - min_{l,r})\) ,即所有子区间最大值之和减去所有子区间最小值之和。枚举端点用单调队列维护最小最大值,复杂度是 \(O(n^2)\)。换一个角度考虑,把每个元素当作最大/最小值,看看能维持左右多长,再将端点数相乘即可,这里用单调栈维护最邻近大于/小于。

细节上注意,以最大值为例,如果两个相同的元素,!s1.empty() && a[s1.top()] <= a[i] 都用小于等于作为弹出条件,那么这两个元素的扩展区间是完全相同的,从而左侧元素的右侧扩展和右侧元素的左侧扩展会有重复,因此保留一侧扩展而另一侧到小于等于就停,即 !s2.empty() && a[s2.top()] < a[i]

并且长度为 \(1\) 的区间不能算入其中,因此区间总数是 \((r[i] - i + 1) * (i - l[i] + 1) - 1\) ,但是由于长度为一的区间在最大值减去最小值过程会被消去,因此不减一也行。

这里有个小技巧,找最邻近大于时,即最小值扩展距离,可以将原数组取相反数,此时最小的会变成最大的,可以用最大值的方式求取,最后答案取相反数即可。

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

int n;
int a[100007], l[100007], r[100007];

ll calc() {
    stack<int> s1, s2;
    for (int i = 0;i < n;i++) {
        while (!s1.empty() && a[s1.top()] <= a[i]) s1.pop();
        l[i] = s1.empty() ? 0 : s1.top() + 1;
        s1.push(i);
    }
    for (int i = n - 1;i >= 0;i--) {
        while (!s2.empty() && a[s2.top()] < a[i]) s2.pop();///防止相等元素重复计算
        r[i] = s2.empty() ? n - 1 : s2.top() - 1;
        s2.push(i);
    }
    ll sum = 0;
    for (int i = 0;i < n;i++)
        sum += (1LL * (r[i] - i + 1) * (i - l[i] + 1) - 1) * a[i];
    return sum;
}

bool solve() {

    cin >> n;
    for (int i = 0;i < n;i++) cin >> a[i];
    ll mx = calc();
    for (int i = 0;i < n;i++) a[i] = -a[i];///取相反数,最小值变最大值可以用同一种操作
    ll mn = -calc();
    cout << mx - mn << '\n';
    return true;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    cin >> t;
    while (t--) {
        if (!solve()) cout << -1 << '\n';
    }
    return 0;
}
posted @ 2022-07-02 21:40  空白菌  阅读(48)  评论(0)    收藏  举报