和Hnoi2016的序列差不多,但这道题用莫队会被卡成30分。
把问题抽象到
n×n
n
×
n
的二维平面上,二维平面上
(i,j)
(
i
,
j
)
位置的数值表示灵魂对
i,j
i
,
j
提供的攻击力(
i≥j
i
≥
j
时
(i,j)
(
i
,
j
)
位置的数值为
0
0
)。
由题意,对于灵魂对:
1、如果
k[i]
k
[
i
]
和
k[j]
k
[
j
]
中一个是区间
[i,j]
[
i
,
j
]
的最大值,另一个是区间
[i,j]
[
i
,
j
]
的次大值,则提供的攻击力为
p1
p
1
;
2、如果
k[i]
k
[
i
]
和
k[j]
k
[
j
]
中一个是区间
[i,j]
[
i
,
j
]
的最大值,另一个不是区间
[i,j]
[
i
,
j
]
的次大值,则提供的攻击力为
p2
p
2
。
先预处理出
i
i
往左第一个比大的位置
prei
p
r
e
i
,和
i
i
往右第一个比大的位置
sufi
s
u
f
i
。
先不考虑另一个端点是否为次大值。易得:
对于任何一个
i
i
,左端点在,右端点为
i
i
,和左端点为,右端点在
[i+1,sufi−1]
[
i
+
1
,
s
u
f
i
−
1
]
的区间的最大值为
k[i]
k
[
i
]
。
所以,预处理修改时,将横坐标为
[prei+1,i−1]
[
p
r
e
i
+
1
,
i
−
1
]
,纵坐标为
i
i
的矩形内的数加上,再将横坐标为
i
i
,纵坐标为的矩形内的数也加上
p2
p
2
。
同时注意到,上面的处理忽略了另一个端点为次大值的情况,这种情况更好处理:
对于任何一个
i
i
,在区间
[prei,i]
[
p
r
e
i
,
i
]
和区间
[i,sufi]
[
i
,
s
u
f
i
]
为次大值。
也就是将坐标
(prei,i)
(
p
r
e
i
,
i
)
和
(i,sufi)
(
i
,
s
u
f
i
)
两个坐标的数值加上
p1−p2
p
1
−
p
2
。
询问就是查询一个矩形内所有数的和。
怎样实现呢?当然是用万能的线段树了!离线排序时候扫描线+线段树解决。
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define p2 p << 1
#define p3 p << 1 | 1
using namespace std;
inline int read() {
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
typedef long long ll;
const int N = 2e5 + 5, M = 1e6 + 5;
int n, m, P1, P2, a[N], stk[N], top, pre[N], suf[N], tot;
struct cyx {
int t, l, r, dx, dy; cyx() {}
cyx(int _t, int _l, int _r, int _dx, int _dy) :
t(_t), l(_l), r(_r), dx(_dx), dy(_dy) {}
} orz[M];
struct pyz {int l, r, id;} que[N]; ll sumx[M], sumy[M], addx[M], addy[M], ans[N];
inline bool comp1(const cyx &a, const cyx &b) {return a.t > b.t;}
inline bool comp2(const pyz &a, const pyz &b) {return a.l > b.l;}
inline void down(int p) {
addx[p2] += addx[p]; addx[p3] += addx[p]; addx[p] = 0;
addy[p2] += addy[p]; addy[p3] += addy[p]; addy[p] = 0;
}
inline void upt(int l, int r, int p) {
int mid = l + r >> 1; sumx[p] = sumx[p2] + sumx[p3] + addx[p2] *
(mid - l + 1) + addx[p3] * (r - mid); sumy[p] = sumy[p2] + sumy[p3]
+ addy[p2] * (mid - l + 1) + addy[p3] * (r - mid);
}
inline void change(int l, int r, int s, int e, int dx, int dy, int p) {
if (l == s && r == e) return (void) (addx[p] += dx, addy[p] += dy);
int mid = l + r >> 1; down(p);
if (e <= mid) change(l, mid, s, e, dx, dy, p2);
else if (s >= mid + 1) change(mid + 1, r, s, e, dx, dy, p3);
else change(l, mid, s, mid, dx, dy, p2),
change(mid + 1, r, mid + 1, e, dx, dy, p3); upt(l, r, p);
}
inline ll ask(int l, int r, int s, int e, int x, int p) {
if (l == s && r == e) return (sumx[p] + addx[p] * (r - l + 1))
* x + (sumy[p] + addy[p] * (r - l + 1));
int mid = l + r >> 1; down(p); ll res;
if (e <= mid) res = ask(l, mid, s, e, x, p2);
else if (s >= mid + 1) res = ask(mid + 1, r, s, e, x, p3);
else res = ask(l, mid, s, mid, x, p2) + ask(mid + 1, r, mid + 1, e, x, p3);
return upt(l, r, p), res;
}
int main() {
int i, j; n = read(); m = read(); P1 = read(); P2 = read();
for (i = 1; i <= n; i++) a[i] = read();
stk[top = 0] = 0; for (i = 1; i <= n; i++) {
while (top && a[stk[top]] < a[i]) top--;
pre[stk[++top] = i] = stk[top - 1];
}
stk[top = 0] = n + 1; for (i = n; i; i--) {
while (top && a[stk[top]] < a[i]) top--;
suf[stk[++top] = i] = stk[top - 1];
}
for (i = 1; i <= n; i++) {
if (i + 1 != suf[i]) orz[++tot] = cyx(i, i + 1, suf[i] - 1, 0, P2);
if (pre[i]) orz[++tot] = cyx(pre[i], i, i, 0, P1 - P2);
if (suf[i] <= n) orz[++tot] = cyx(i, suf[i], suf[i], 0, P1 - P2);
if (pre[i] + 1 != i) {
orz[++tot] = cyx(i - 1, i, i, -P2, i * P2);
orz[++tot] = cyx(pre[i], i, i, P2, -(pre[i] + 1) * P2);
}
}
for (i = 1; i <= m; i++) que[i].l = read(), que[i].r = read(), que[i].id = i;
sort(orz + 1, orz + tot + 1, comp1); sort(que + 1, que + m + 1, comp2);
for (i = j = 1; i <= m; i++) {
while (j <= tot && orz[j].t >= que[i].l)
change(1, n, orz[j].l, orz[j].r, orz[j].dx, orz[j].dy, 1), j++;
ans[que[i].id] = ask(1, n, que[i].l, que[i].r, que[i].l, 1);
}
for (i = 1; i <= m; i++) printf("%lld\n", ans[i]);
return 0;
}