nowcoder 区区区间间间 (单调栈)

链接:https://ac.nowcoder.com/acm/problem/20806
来源:牛客网

题目描述

给出长度为n的序列a,其中第i个元素为a[i]
区间[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<=20,n<=1e5,a[i]<=1e5

思路:

问题转化为:
在这里插入图片描述

首先要发现题目式子里面的区间长度至少为2

利用单调栈求出每个数:
1. 作为最大值的左右拓展范围 l[i],r[i]
a[i]有贡献的区间有三种情况:
    1. 左端点为l[i]到i-1 任意一点,右端点为i
    2. 左端点为i,右端点为i+1 到r[i]任意一点
    3. 左端点为l[i]到 i-1 任意一点, 右端点为i+1 到r[i]任意一点
    总区间数 num=(r[i]-i+1-1)+(i-l[i]+1-1)+(r[i]-i+1-1)*(i-l[i]+1-1);
    答案累加a[i]*num;

2. 作为最小值的左右拓展范围
最小值同上,减去就行了

还有一个坑点就是如果数组中相邻元素重复的情况会导致某些数值重复计算,为了防止重复,
求左右拓展范围的时候第一遍扫不能带等号,第二遍扫带等号(或者第一遍带等号,第二遍不带)
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
using namespace std;
#define int long long
const int inf=1e9;
const int maxm=1e5+5;
int a[maxm];
int l[maxm],r[maxm];
stack<int>stk;
int ans;
int n;
void getma(){//求a[i]为最大值的拓展范围
    a[0]=a[n+1]=inf;//保证全部元素出栈
    while(!stk.empty())stk.pop();
    for(int i=1;i<=n+1;i++){//右
        while(!stk.empty()&&a[stk.top()]<a[i]){
            r[stk.top()]=i-1;
            stk.pop();
        }
        stk.push(i);
    }
    while(!stk.empty())stk.pop();
    for(int i=n;i>=0;i--){//左
        while(!stk.empty()&&a[stk.top()]<=a[i]){
            l[stk.top()]=i+1;
            stk.pop();
        }
        stk.push(i);
    }
    for(int i=1;i<=n;i++){
        int temp=(r[i]-i+1-1)+(i-l[i]+1-1)+(r[i]-i+1-1)*(i-l[i]+1-1);
        ans+=temp*a[i];
    }
}
void getmi(){//求a[i]为最小值的拓展范围
    a[0]=a[n+1]=-1;//保证全部元素出栈
    while(!stk.empty())stk.pop();
    for(int i=1;i<=n+1;i++){//右
        while(!stk.empty()&&a[stk.top()]>a[i]){
            r[stk.top()]=i-1;
            stk.pop();
        }
        stk.push(i);
    }
    while(!stk.empty())stk.pop();
    for(int i=n;i>=0;i--){//左
        while(!stk.empty()&&a[stk.top()]>=a[i]){
            l[stk.top()]=i+1;
            stk.pop();
        }
        stk.push(i);
    }
    for(int i=1;i<=n;i++){
        int temp=(r[i]-i+1-1)+(i-l[i]+1-1)+(r[i]-i+1-1)*(i-l[i]+1-1);
        ans-=temp*a[i];
    }
}
signed main(){
    int T;
    cin>>T;
    while(T--){
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        ans=0;
        getma();
        getmi();
        cout<<ans<<endl;
    }
    return 0;
}
//https://ac.nowcoder.com/acm/problem/20806
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值