[HNOI2016]序列

题面

给你一个数列,很多组询问,每次询问 \(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]);
}

猜你喜欢

转载自www.cnblogs.com/HNYLMSTea/p/10598016.html