题意是给你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;
}