【HNOI2017】影魔

题面

题解

对于两个位置\(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;
}

猜你喜欢

转载自www.cnblogs.com/cj-xxz/p/10435269.html