[HNOI2016]序列
阿新 • • 發佈:2019-03-26
tor https har using ons sin lld math 一個數
的子區間最小值都是 \(a[p]\) ,\([p+1,r]\dots[r,r]\) 的貢獻就是 \(f[r] - f[p]\) 。
題面
給你一個數列,很多組詢問,每次詢問 \(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]\)
\({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]序列