题面
给你一个数列,很多组询问,每次询问 \(l\) 到 \(r\) 的子区间的区间最小值之和。
\(\text{Solution:}\)
这种没有修改的,与子区间有关的题,不禁让我想起了影魔显然要离线做。
先用单调栈预处理出每个点往左往右第一个比它小的位置 \(L[i],R[i]\)。
记 \(f[i]\) 为以 \(i\) 结尾的区间最小值之和,
显然有转移 : \(f[i] = f[L[i]] + a[i]\times(i-L[i])\)
同理,\(g[i]\) 为以 \(i\) 开头的区间的最小值之和,
有 \(g[i] = g[R[i]] + a[i] \times (R[i] - i)\)
考虑在莫队双指针移动的时候,计算区间内以右指针结尾的子区间的贡献,以及区间内以左指针开头的子区间的贡献,至于是 \(+\) 还是 \(-\) 贡献,由指针移动方向而定。
这里举一个右指针右移的例子:
ans += calcR(l, ++r);
LL calcR(int l, int r) {
int p = query(l, r);
return a[p] * (p - l + 1) + f[r] - f[p];
}
这个函数就是计算以 \(r+1\) (没动之前为 \(r\) ) 结尾的子区间的贡献,\(p\) 为最小值的位置,显然 \([l,r],[l+1,r]\dots [p,r]\) 的子区间最小值都是 \(a[p]\) ,\([p+1,r]\dots[r,r]\) 的贡献就是 \(f[r] - f[p]\) 。
\({Source:}\)
#include <set>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <assert.h>
#include <algorithm>
using namespace std;
#define LL long long
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define GO debug("GO\n")
inline int rint() {
register int x = 0, f = 1; register char c;
while (!isdigit(c = getchar())) if (c == '-') f = -1;
while (x = (x << 1) + (x << 3) + (c ^ 48), isdigit(c = getchar()));
return x * f;
}
template<typename T> inline void chkmin(T &a, T b) { a > b ? a = b : 0; }
template<typename T> inline void chkmax(T &a, T b) { a < b ? a = b : 0; }
const int N = 1e5 + 10;
#define pii pair<int, int>
int n, m, a[N], L[N], R[N];
LL f[N], g[N];
struct Query {
int l, r, id;
bool operator< (const Query &b) const {
return (l >> 8) == (b.l >> 8) ? r < b.r : l < b.l;
}
} q[N];
namespace ST {
pii st[N][22];
void build() {
for (int i = 1; i <= n; ++ i) st[i][0] = make_pair(a[i], i);
for (int i = 1; n >> i; ++ i)
for (int j = 1; j + (1 << i) - 1 <= n; ++ j)
st[j][i] = min(st[j][i - 1], st[j + (1 << i - 1)][i - 1]);
}
int query(int l, int r) {
int k = log2(r - l + 1);
return min(st[l][k], st[r - (1 << k) + 1][k]).second;
}
}using ST::query;
LL ans;
LL calcR(int l, int r) {
int p = query(l, r);
return 1ll * (p - l + 1) * a[p] + f[r] - f[p];
}
LL calcL(int l, int r) {
int p = query(l, r);
return 1ll * (r - p + 1) * a[p] + g[l] - g[p];
}
LL res[N];
int main() {
#ifndef ONLINE_JUDGE
freopen("xhc.in", "r", stdin);
freopen("xhc.out", "w", stdout);
#endif
n = rint(), m = rint();
for (int i = 1; i <= n; ++ i)
a[i] = rint();
for (int i = 1; i <= m; ++ i) {
q[i].l = rint(), q[i].r = rint();
q[i].id = i;
}
static int stk[N], top;
top = 0;
for (int i = 1; i <= n; ++ i) {
while (top and a[stk[top]] > a[i])
top --;
L[i] = stk[top];
stk[++top] = i;
}
stk[top = 0] = n + 1;
for (int i = n; i >= 1; -- i) {
while (top and a[stk[top]] > a[i])
top --;
R[i] = stk[top];
stk[++top] = i;
}
for (int i = 1; i <= n; ++ i)
f[i] = f[L[i]] + 1ll * (i - L[i]) * a[i];
for (int i = n; i >= 1; -- i)
g[i] = g[R[i]] + 1ll * (R[i] - i) * a[i];
ST::build();
sort(q + 1, q + 1 + m);
int l = 1, r = 0;
for (int i = 1; i <= m; ++ i) {
while (r < q[i].r) r++, ans += calcR(l, r);
while (r > q[i].r) ans -= calcR(l, r), r--;
while (l < q[i].l) ans -= calcL(l, r), l++;
while (l > q[i].l) l--, ans += calcL(l, r);
res[q[i].id] = ans;
}
for (int i = 1; i <= m; ++ i) printf("%lld\n", res[i]);
}