51nod 1215 数组的宽度(单调栈)

1215 数组的宽度

题目

N个整数组成的数组,定义子数组a[i]…a[j]的宽度为:max(a[i]…a[j]) - min(a[i]…a[j]),求所有子数组的宽度和。

输入

第1行:1个数N,表示数组的长度。(1 <= N <= 50000)
第2 - N + 1行:每行1个数,表示数组中的元素(1 <= A[i] <= 50000)

输出

输出所有子数组的宽度和。

输入样例

5
1
2
3
4
5

输出样例

20

代码

5入栈,前面所有比5小的出栈,并将右边界设为5 => 5 (确定了1的右边界是5,对应下标为1)
4入栈,前面所有比4小的出栈,并将右边界设为4 => 5 4
2入栈,前面所有比2小的出栈,并将右边界设为2 => 5 4 2
3入栈,前面所有比3小的出栈,并将右边界设为3 => 5 4 3(确定了2的右边界是3,对应下标为4)
最后所有数出栈,将5 4 3这3个数的右边界的下标设为5。

这样可以确认,一次操作中每个数字最多进一次栈出一次栈,所有复杂度是O(n)的。
用两次单调栈一次确定该数作为区间最大值的使用区间,一次确定该数作为区间最小值的使用区间。

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<math.h>
#include<stack>
#include<set>
#include<queue>

using namespace std;
typedef long long LL;
typedef struct node {
    int x;
    int id;
} ss;
LL ans[60000];
// 当前数作为最大的数左右范围
int ll[60000];
int rr[60000];
stack<ss> stcx;

int main() {
    int n, m;
    while (scanf("%d", &n) != EOF) {
        int i, j;
        LL sum = 0;
        for (i = 1; i <= n; i++) {
            scanf("%lld", &ans[i]);
        }
        while (!stcx.empty()) {
            stcx.pop();
        }
        for (i = 1; i <= n; i++) {
            while (!stcx.empty()) {
                ss ad = stcx.top();
                if (ad.x >= ans[i]) {
                    ss ak;
                    ll[i] = ad.id + 1;
                    ak.id = i;
                    ak.x = ans[i];
                    stcx.push(ak);
                    break;
                } else {
                    stcx.pop();
                    rr[ad.id] = i - 1;
                }
            }
            if (stcx.empty()) {
                ll[i] = 1;
                ss ak;
                ak.x = ans[i];
                ak.id = i;
                stcx.push(ak);
            }
        }
        while (!stcx.empty()) {
            ss ak = stcx.top();
            stcx.pop();
            rr[ak.id] = n;
        }

        for (i = 1; i <= n; i++) {
            sum += ans[i] * ((rr[i] - i + 1) * (i - ll[i] + 1));
        }
        for (i = 1; i <= n; i++) {
            while (!stcx.empty()) {
                ss ad = stcx.top();
                // 修改此处条件,求左区间
                if (ad.x <= ans[i]) {
                    ss ak;
                    ll[i] = ad.id + 1;
                    ak.id = i;
                    ak.x = ans[i];
                    stcx.push(ak);
                    break;
                } else {
                    stcx.pop();
                    rr[ad.id] = i - 1;
                }
            }
            if (stcx.empty()) {
                ll[i] = 1;
                ss ak;
                ak.x = ans[i];
                ak.id = i;
                stcx.push(ak);
            }
        }
        while (!stcx.empty()) {
            ss ak = stcx.top();
            stcx.pop();
            rr[ak.id] = n;
        }
        for (i = 1; i <= n; i++) {
            sum -= ans[i] * ((rr[i] - i + 1) * (i - ll[i] + 1));
        }
        printf("%lld\n", sum);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值