codeforces 601B (单调栈)

题意是给你n个数,和q个询问,每次都求出L到R中所有子区间的L(l, r)的和。

首先L函数可以看成是两点之间的倾角或者俯角,在一个区间里倾角或者俯角最大的一定是两个相邻的点。

证明:对于三个点i, j, k(i<j<k),假设i到k的角度最大, 那么i到k的角度大于i到j的角度,

(1),整理以后得到k*h[j] - k*h[i] - i*h[j] < j*h[k] - j*h[i] - i*h[k],

得到,也就是j到k 的角度大于i到k的角度,这和假设产生了矛盾。

当然如果(1)式的不等号后半部分分子为负, 还是可以得出一个矛盾的结论。

事实上,画个图就能很明显的看出矛盾,就只有两种情况

然后只需要一个区间里面最大的斜率绝对值,转化成了一个经典的单调栈题目。

只需要搞出两个相邻点的斜率,以它为最大值,向左边最大扩展到l[i]处, 向右边最大扩展到r[i]处,那么这个区间里面的所有子区间都是以i处的斜率为最大值,对于整个区间的贡献就是(i-l[i]+1)*(r[i]-i+1)*i处的斜率。

复杂度O(n*q)。

#include <bits/stdc++.h>
using namespace std;
#define maxn 111111

long long a[maxn];
struct node {
    long long num;
    int pos;
}p[maxn];
int q, n;
long long l[maxn], r[maxn];
stack <node> gg;

int main () {
    //freopen ("in", "r", stdin);
    scanf ("%d%d", &n, &q);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i <= n-1; i++) {
        p[i].num = abs (a[i]-a[i-1]);
        p[i].pos = i;
    }
    while (q--) {
        int L, R;
        scanf ("%d%d", &L, &R);
        R--;
        while (!gg.empty ())
            gg.pop ();
        for (int i = L; i <= R; i++) {
            if (gg.empty ())
                gg.push (p[i]);
            else {
                while (!gg.empty ()) {
                    node now = gg.top ();
                    if (now.num <= p[i].num) {
                        r[now.pos] = p[i].pos-1;
                        gg.pop ();
                    }
                    else
                        break;
                }
                gg.push (p[i]);
            }
        }
        node x = gg.top ();
        while (!gg.empty ()) {
            node now = gg.top (); gg.pop ();
            r[now.pos] = x.pos;
        } 

        for (int i = R; i >= L; i--) {
            if (gg.empty ())
                gg.push (p[i]);
            else {
                while (!gg.empty ()) {
                    node now = gg.top ();
                    if (now.num < p[i].num) {
                        l[now.pos] = p[i].pos+1;
                        gg.pop ();
                    }
                    else
                        break;
                }
                gg.push (p[i]);
            }
        }
        x = gg.top ();
        while (!gg.empty ()) {
            node now = gg.top (); gg.pop ();
            l[now.pos] = x.pos;
        } 

        long long ans = 0;
        for (int i = L; i <= R; i++) {
            ans += (i-l[i]+1)*(r[i]-i+1)*p[i].num;
        }
        cout << ans << endl;
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值