1. 程式人生 > >[HNOI 2017]影魔

[HNOI 2017]影魔

operator 之間 www. 一段 掃描 隊列 urn blog scan

Description

題庫鏈接

給你一段長度為 \(n\) 的序列 \(K\)\(m\) 組詢問,每次給定左右端點 \(l,r\) 。求出滿足區間內下述貢獻和。

  1. 如果一個區間的兩個端點是這一個區間的最大與次大值,那麽將獲得 \(p_1\) 的價值;
  2. 如果一個區間的一個端點是最大值,而另一個端點不是次大值,那麽將獲得 \(p_2\) 的價值。

\(1\leq n,m\leq 200000\)

Solution

顯然,兩種情況都需要滿足其中一個端點是最大值。我們可以用單調棧預處理出兩個數組 \(l_i,r_i\) 分別表示左邊第一個比 \(K_i\) 大的數的位置,以及右邊第一個比 \(K_i\)

大的數的位置。

顯然我們枚舉位置 \(i\) 時,滿足:

  1. 左端點為 \(l_i\) 右端點為 \(r_i\) 時,這個區間貢獻為 \(p_1\)
  2. 左端點為 \(l_i\) 右端點在 \((i,r_i)\) 之間時,貢獻為 \(p_2\)
  3. 左端點在 \((l_i, i)\) 之間時,右端點為 \(r_i\) ,貢獻為 \(p_2\)

然後就是掃描線來處理所有詢問了。

因為單調隊列的 \(while\) 寫成 \(if\) 調了一下午。

Code

//It is made by Awson on 2018.3.6
#include <bits/stdc++.h>
#define LL long long
using namespace std; const int N = 200000; int n, m, p1, p2, a[N+5], l[N+5], r[N+5], S[N+5], top, cnt; LL ans[N+5]; struct Segment_tree { #define lr(o) (o<<1) #define rr(o) (o<<1|1) LL key[(N<<2)+5], lazy[(N<<2)+5]; void pushdown(int o, int l, int r, int mid) { key[lr(o)] += 1ll
*(mid-l+1)*lazy[o]; key[rr(o)] += 1ll*(r-mid)*lazy[o]; lazy[lr(o)] += lazy[o], lazy[rr(o)] += lazy[o]; lazy[o] = 0; } void update(int o, int l, int r, int a, int b, int k) { if (a <= l && r <= b) {key[o] += 1ll*(r-l+1)*k, lazy[o] += k; return; } int mid = (l+r)>>1; if (lazy[o]) pushdown(o, l, r, mid); if (a <= mid) update(lr(o), l, mid, a, b, k); if (b > mid) update(rr(o), mid+1, r, a, b, k); key[o] = key[lr(o)]+key[rr(o)]; } LL query(int o, int l, int r, int a, int b) { if (a <= l && r <= b) return key[o]; int mid = (l+r)>>1; if (lazy[o]) pushdown(o, l, r, mid); LL c1 = 0, c2 = 0; if (a <= mid) c1 = query(lr(o), l, mid, a, b); if (b > mid) c2 = query(rr(o), mid+1, r, a, b); return c1+c2; } }T; struct opts { int l, r, t, id, p; bool operator < (const opts &b) const {return t < b.t; } }s1[N*2+5], s2[N*3+5]; void work() { scanf("%d%d%d%d", &n, &m, &p1, &p2); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i <= m; i++) { int l, r; scanf("%d%d", &l, &r); ans[i] = 1ll*(r-l)*p1; s1[i].l = l, s1[i].r = r, s1[i].t = l-1, s1[i].id = i, s1[i].p = -1; s1[i+m].l = l, s1[i+m].r = r, s1[i+m].t = r, s1[i+m].id = i, s1[i+m].p = 1; } top = 0; for (int i = 1; i <= n; i++) { while (top > 0 && a[i] > a[S[top]]) --top; l[i] = (top == 0 ? 0 : S[top]); S[++top] = i; } top = 0; for (int i = n; i >= 1; i--) { while (top > 0 && a[i] > a[S[top]]) --top; r[i] = top == 0 ? n+1 : S[top]; S[++top] = i; } for (int i = 1; i <= n; i++) { if (l[i] != 0 && r[i] != n+1) s2[++cnt].l = s2[cnt].r = r[i], s2[cnt].t = l[i], s2[cnt].p = p1; if (l[i] != 0 && r[i] > i+1) s2[++cnt].l = i+1, s2[cnt].r = r[i]-1, s2[cnt].t = l[i], s2[cnt].p = p2; if (l[i] < i-1 && r[i] != n+1) s2[++cnt].l = l[i]+1, s2[cnt].r = i-1, s2[cnt].t = r[i], s2[cnt].p = p2; } sort(s1+1, s1+2*m+1); sort(s2+1, s2+cnt+1); int n1 = 1, n2 = 1; while (n1 <= 2*m) { while (n2 <= cnt && s2[n2].t <= s1[n1].t) T.update(1, 1, n, s2[n2].l, s2[n2].r, s2[n2].p), ++n2; while (n1 <= 2*m && (s1[n1].t < s2[n2].t || n2 > cnt)) ans[s1[n1].id] += 1ll*T.query(1, 1, n, s1[n1].l, s1[n1].r)*s1[n1].p, ++n1; } for (int i = 1; i <= m; i++) printf("%lld\n", ans[i]); } int main() { work(); return 0; }

[HNOI 2017]影魔