1. 程式人生 > >[HNOI2017] 影魔

[HNOI2017] 影魔

Description

Portal

一句話題意:給定n個數的排列,m次詢問,每次詢問詢問一個區間內所有子區間的貢獻。
每個區間如果兩個端點分別是最大值次大值,我們就算P1的貢獻。
如果兩個端點一個是最大值,一個不是次大值,我們就算P2的貢獻。

$ n, m \leq 200009 $

Solution

這題其實是除了禮物之外的HNOI的最容易的題目

因為要處理兩個端點的值, 所以我們欽定某一個端點為最大值(這裡欽定左端點)
,同時處理另一個端點的情況。

那麼如果要計算左端點是最大值,那麼只要把序列反轉然後重新計算一次。

考慮計算一個點的值。如果某個點是從右端點出發的當前的最大值。
那麼這個點x要和右端點計算P1

的貢獻。這個點之前到上個最大點之後的點全部計算P2的貢獻。
這個可以用個單調棧來維護。

最後還有一部分細節: 在單調棧彈完的時候,必須要把之前的彈出的沒處理的元素加上P2的貢獻。

Inspiration

在詢問兩個端點都有要求的時候,我們可以考慮一個固定端點然後處理。
查詢兩遍即可。

Code

#include<bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef long double LD;
int read() {
    char ch = getchar();
    int x = 0, flag = 1;
    for (;!isdigit(ch); ch = getchar()) if (ch == '-') flag *= -1;
    for (;isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    return x * flag;
}
void write(int x) {
    if (x < 0) putchar('-'), x = -x;
    if (x >= 10) write(x / 10);
    putchar(x % 10 + 48);
}

const int Maxn = 2e5 + 9;
int n, m, a[Maxn], p1, p2;
vector <pair<int, int> > querySeq[Maxn], seq;
LL ans[Maxn];

void init() {
    n = read(), m = read(), p1 = read(), p2 = read();
    rep (i, 1, n) a[i] = read();
    rep (i, 1, m) {
        int l = read(), r = read();
        seq.push_back(make_pair(l, r));
    }
}

namespace SGMTtree {
    LL tree[Maxn << 3], add[Maxn << 3];

#define lc(x) ((x) << 1)
#define rc(x) ((x) << 1 | 1)
#define ls rt << 1, l, mid
#define rs rt << 1 | 1, mid + 1, r

    inline LL getTree(int rt) { return tree[rt];}
    LL (*get)(int);

    inline void setAdd(int rt, int l, int r, int v) { 
        add[rt] += v;
        tree[rt] += (r - l + 1ll) * v;
    }
    void (*setTag)(int, int, int, int);
    void init(int modif, int quer) { 
        clar(tree, 0); 
        clar(add, 0); 
        if (modif == 1) setTag = setAdd;
        if (quer == 1) get = getTree;
    }

    inline void pushup(int rt) { tree[rt] = tree[lc(rt)] + tree[rc(rt)]; }
    inline void pushdown(int rt, int l, int r) {
        int mid = (l + r) >> 1;
        if (add[rt]) {
            setAdd(lc(rt), l, mid, add[rt]);
            setAdd(rc(rt), mid + 1, r, add[rt]);
            add[rt] = 0;
        }
    }

    void modify(int rt, int l, int r, int p, int q, int v) {
        if (p > q) return ;
        if (p <= l && r <= q) {
            setTag(rt, l, r, v);
            return ;
        }
        int mid = (l + r) >> 1; pushdown(rt, l, r);
        if (q <= mid)  modify(ls, p, q, v);
        else if (p >= mid + 1)  modify(rs, p, q, v);
        else modify(ls, p, q, v), modify(rs, p, q, v);
        pushup(rt);
    } 
    LL query(int rt, int l, int r, int p, int q) {
        if (p > q) return 0;
        if (p <= l && r <= q) return get(rt);

        int mid = (l + r) >> 1; pushdown(rt, l, r);

        if (q <= mid) return query(ls, p, q);
        else if (p >= mid + 1) return query(rs, p, q);
        else return query(ls, p, q) + query(rs, p, q);
    }

#undef lc
#undef rc
#undef ls
#undef rs
}

void duce() {
    stack <int> s;
    SGMTtree :: init(1, 1);
    rep (i, 1, n) {
        int _lst = i;
        while (!s.empty() && a[s.top()] <= a[i]) {
            if (s.top() + 1 < _lst) SGMTtree :: modify(1, 1, n, s.top() + 1, _lst - 1, p2);
            SGMTtree :: modify(1, 1, n, s.top(), s.top(), p1);
            _lst = s.top(); s.pop();
        }
        if (s.empty()) SGMTtree :: modify(1, 1, n, 1, _lst - 1, p2);
        else SGMTtree :: modify(1, 1, n, s.top() + 1, _lst - 1, p2);
        s.push(i);
        rep (j, 0, querySeq[i].size() - 1) 
            ans[querySeq[i][j].second] += SGMTtree :: query(1, 1, n, querySeq[i][j].first, i);
    }
}

void solve() {
    rep (i, 0, m - 1)   querySeq[seq[i].second].push_back(make_pair(seq[i].first, i + 1));
    rep (i, 1, n) sort(querySeq[i].begin(), querySeq[i].end());
    duce();

    rep (i, 1, n) querySeq[i].clear();
    rep (i, 0, m - 1) querySeq[n - seq[i].first + 1].push_back(make_pair(n - seq[i].second + 1, i + 1));
    rep (i, 1, n) sort(querySeq[i].begin(), querySeq[i].end());
    reverse(a + 1, a + n + 1);
    duce();

    rep (i, 1, m) printf("%lld\n", ans[i]);
}

int main() {
    freopen("LG3722.in", "r", stdin);
    freopen("LG3722.out", "w", stdout);

    init();
    solve();

#ifdef Qrsikno
    debug("\nRunning time: %.3lf(s)\n", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
    return 0;
}