[HNOI 2017]影魔
阿新 • • 發佈:2018-03-06
operator 之間 www. 一段 掃描 隊列 urn blog scan 大的數的位置。
Description
題庫鏈接
給你一段長度為 \(n\) 的序列 \(K\) 。 \(m\) 組詢問,每次給定左右端點 \(l,r\) 。求出滿足區間內下述貢獻和。
- 如果一個區間的兩個端點是這一個區間的最大與次大值,那麽將獲得 \(p_1\) 的價值;
- 如果一個區間的一個端點是最大值,而另一個端點不是次大值,那麽將獲得 \(p_2\) 的價值。
\(1\leq n,m\leq 200000\)
Solution
顯然,兩種情況都需要滿足其中一個端點是最大值。我們可以用單調棧預處理出兩個數組 \(l_i,r_i\) 分別表示左邊第一個比 \(K_i\) 大的數的位置,以及右邊第一個比 \(K_i\)
顯然我們枚舉位置 \(i\) 時,滿足:
- 左端點為 \(l_i\) 右端點為 \(r_i\) 時,這個區間貢獻為 \(p_1\) ;
- 左端點為 \(l_i\) 右端點在 \((i,r_i)\) 之間時,貢獻為 \(p_2\) ;
- 左端點在 \((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]影魔