1. 程式人生 > >[HNOI2016]序列

[HNOI2016]序列

tor https har using ons sin lld math 一個數

題面

給你一個數列,很多組詢問,每次詢問 \(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]);
}

[HNOI2016]序列