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
", ans[i]);
}
int main() {
freopen("LG3722.in", "r", stdin);
freopen("LG3722.out", "w", stdout);
init();
solve();
#ifdef Qrsikno
debug("
Running time: %.3lf(s)
", clock() * 1.0 / CLOCKS_PER_SEC);
#endif
return 0;
}