题面
题解
对于两个位置\(l, r\),如果它们分别是区间\([l, r]\)的最大值,那么可以产生\(p1\)的贡献,
否则如果它们中有一个是最大值,那么可以产生\(p2\)的贡献。
所以对于当前位置\(i\),假设左右两边第一个比它大的是\(l, r\),那么\([l, r]\)可以产生p1的贡献,\([l + 1 \sim i - 1, r]\)和\([l, i + 1\sim r - 1]\)会产生\(p2\)的贡献。
于是加完\(R[i]\)后,对\(L[i]\)产生\(p1\)的贡献
加完位置\(L[i]\)后,对\((i, R[i])\)产生\(p2\)的贡献
加完位置\(R[i]\)后,对\((L[i], i)\)产生\(p2\)的贡献
那么对于一个询问\(L, R\),只要考虑它范围内的贡献,于是也拆成三个部分
加入完\(L - 1\)时,要忽略前面\(L, R\)位置产生的所有贡献
加入完\(R\)时,要考虑所有的\(L, R\)位置产生的贡献
还有每一组的\([i, i + 1]\)产生的贡献\(p1\)
这样的话,用扫描线+区间加法+区间求和即可。
用单调栈和树状数组就行了。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define clear(x, y) memset(x, y, sizeof(x))
namespace IO
{
const int BUFSIZE = 1 << 20;
char ibuf[BUFSIZE], *is = ibuf, *it = ibuf;
inline char getchar() { if (is == it) it = (is = ibuf) + fread(ibuf, 1, BUFSIZE, stdin); return *is++; }
}
inline int read()
{
int data = 0, w = 1;
char ch = IO::getchar();
while(ch != '-' && (ch < '0' || ch > '9')) ch = IO::getchar();
if(ch == '-') w = -1, ch = IO::getchar();
while(ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = IO::getchar();
return data * w;
}
const int maxn(2e5 + 10);
int n, m, p1, p2, stk[maxn], top, cnt, tot;
int L[maxn], R[maxn], a[maxn];
long long c1[maxn], c2[maxn], ans[maxn];
struct upd { int x, l, r, v; } p[maxn << 2];
struct qry { int x, l, r, v, id; } q[maxn << 2];
inline int operator < (const upd &a, const upd &b) { return a.x < b.x; }
inline int operator < (const qry &a, const qry &b) { return a.x < b.x; }
void add(int x, int w)
{
for(RG int i = x; i <= n; i += i & -i)
c1[i] += w, c2[i] += x * w;
}
long long query(int x)
{
long long ans = 0;
for(RG int i = x; i; i -= i & -i)
ans += (x + 1) * c1[i] - c2[i];
return ans;
}
int main()
{
#ifndef ONLINE_JUDGE
file(cpp);
#endif
n = read(), m = read(), p1 = read(), p2 = read();
for(RG int i = 1; i <= n; i++) a[i] = read();
stk[top = 0] = 0;
for(RG int i = 1; i <= n; i++)
{
while(top && a[stk[top]] < a[i]) --top;
L[i] = stk[top], stk[++top] = i;
}
stk[top = 0] = n + 1;
for(RG int i = n; i; i--)
{
while(top && a[stk[top]] < a[i]) --top;
R[i] = stk[top], stk[++top] = i;
}
for(RG int i = 1; i <= m; i++)
{
int l = read(), r = read(); ans[i] = (r - l) * p1;
q[++cnt] = (qry) {r, l, r, 1, i};
q[++cnt] = (qry) {l - 1, l, r, -1, i};
}
std::sort(q + 1, q + cnt + 1);
for(RG int i = 1; i <= n; i++)
{
if(L[i] && R[i] < n + 1) p[++tot] = (upd) {R[i], L[i], L[i], p1};
if(L[i] && R[i] > i + 1) p[++tot] = (upd) {L[i], i + 1, R[i] - 1, p2};
if(L[i] + 1 < i && R[i] < n + 1)
p[++tot] = (upd) {R[i], L[i] + 1, i - 1, p2};
}
std::sort(p + 1, p + tot + 1);
for(RG int i = 1, j = 1; i <= cnt; i++)
{
while(j <= tot && p[j].x <= q[i].x)
add(p[j].l, p[j].v), add(p[j].r + 1, -p[j].v), ++j;
ans[q[i].id] += q[i].v * (query(q[i].r) - query(q[i].l - 1));
}
for(RG int i = 1; i <= m; i++) printf("%lld\n", ans[i]);
return 0;
}