[HNOI2017] 影魔
阿新 • • 發佈:2018-12-03
Description
一句話題意:給定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; }