每日一题:子串的最大差

子串的最大差 - 题目 - Daimayuan Online Judge

 枚举每个子串不现实,肯定会超时

子串1的最大值-子串1的最小值+子串2的最大值-子串2的最小值+...

即为(子串1的最大值+子串2的最大值+...)-(子串1的最小值+子串2的最小值+...)

可以从贡献度的角度考虑,对于每一个数,求它作为最大值的贡献度和作为最小值的贡献度,所有最大值的贡献度减去所有最小值的贡献度即所求答案

求作为最大值的贡献度,对于某个数,求出它左边比它大的第一个数和右边比它大的第一个数,

例如5 2 2 3 3 4,那么对于第一个3,就是2 2 3 3,它作为最大值的贡献度就是从该数开始(包括它自己)往左数的个数乘从该数开始(包括它自己)往右数的个数(乘法原理),这样算出来的就是以该数作为最大值的包含该值的子串的个数

求作为最小值得贡献度同理

而求一个序列中每个数右边(左边)比它大(小)的第一个数可以用单调栈(具体解释见算法:单调栈-CSDN博客

但是要注意,以求最大为例,在求左边第一个最大和右边第一个最大时,如果有相同的元素的话,是不能两边同时都取等号的,比如4 3 3 3,对于下标为2的3,左边第一个最大下标为1,如果右边第一个下标最大设为5,那么整个满足的子串就是3 3 3,黄色的3对该子串作了贡献

4 3 3 3中下标为3的3,满足的子串是3 3 3,黄色的3对该子串作了贡献,这样就重复了,因为3 3 3是一个子串,它的最大值3对该子串作了贡献,3 3 3这个子串的最大值是3,一个子串只有一个最大值对其做贡献

所以4 3 3 3中我们一边取等号,一边不取等号,对于下标为2的3,满足的子串是3,对于下标为3的3,满足的子串是3 3,对于下标为4的3,满足的子串是3 3 3,这样就能保证同一个子串只记录一次贡献

AC代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<stack>
using namespace std;
typedef long long LL;
const int N = 5e5 + 10;
int a[N];
int n;
int lmax[N], lmin[N];//左边第一个最大的数的下标和第一个最小的数的下标
int rmax[N], rmin[N];//右边第一个最大的数的下标和第一个最小的数的下标
LL res;
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    stack<int>q;
    //栈里面放下标,方便个数的计算
    for (int i = 1; i <= n; i++) {
        while (q.size() && a[q.top()] <= a[i]) q.pop();
        if (!q.size()) lmax[i] = 0;
        else lmax[i] = q.top();
        q.push(i);
    }
    while (q.size()) q.pop();
    for (int i = 1; i <= n; i++) {
        while (q.size() && a[q.top()] >= a[i]) q.pop();
        if (!q.size()) lmin[i] = 0;
        else lmin[i] = q.top();
        q.push(i);
    }
    while (q.size()) q.pop();
    for (int i = n; i >= 1; i--) {
        while (q.size() && a[q.top()] <a[i]) q.pop();
        if (!q.size()) rmax[i] = n+1;
        else rmax[i] = q.top();
        q.push(i);
     }
    while (q.size()) q.pop();
    for (int i = n; i >= 1; i--) {
        while (q.size() && a[q.top()] >a[i]) q.pop();
        if (!q.size()) rmin[i] = n+1;
        else rmin[i] = q.top();
        q.push(i);
    }
    for (int i = 1; i <= n; i++) {
        res += (LL)a[i] * (rmax[i] - i) * (i - lmax[i]) - (LL)a[i] * (rmin[i] - i) * (i - lmin[i]);
    }
    cout<<res<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值