[jzoj5073 GDOI2017第二轮模拟] 影魔
Posted AronQi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[jzoj5073 GDOI2017第二轮模拟] 影魔相关的知识,希望对你有一定的参考价值。
Solution
把所有询问离线
用单调栈分别做两次,求出对于排列数组的单个元素从自己开始到左边和到右边的最远的端点
为了处理贡献对应询问的区间建出一颗线段树
按左端点排序询问,枚举区间内的元素,设当前元素为最大值,那么,从它到区间端点(端点作为最大值)的这个区间的都可以打上p2贡献的标记
设当前元素为次小值,那么,对区间端点打上p1-p2-p2的标记(作为次小有两次重复)
#include <stdio.h> #include <string.h> #include <algorithm> #define _L long long #define _RG register int #define lson s, mid, k << 1 #define rson mid + 1, t, k << 1 | 1 const int N = 200010; int n, m, p1, p2, a[N], l[N], r[N], steck[N], sec, nex[N], tim; _L tag[N << 2], sum[N << 2], ans[N]; struct Q { int l, r, id; }que[N]; inline void downt(_RG s, _RG t, _RG k) { sum[k] += tag[k] * (_L)(t - s + 1); if(s ^ t) tag[k << 1] += tag[k], tag[k << 1 | 1] += tag[k]; tag[k] = 0; } void updat(_RG s, _RG t, _RG k, _RG x, _RG y, _RG z) { _RG mid = s + t >> 1; downt(s, t, k); if(s ^ t) downt(s, mid, k << 1), downt(mid + 1, t, k << 1 | 1); if(s == x && t == y) { tag[k] += z; downt(s, t, k); return; } if(y <= mid) updat(lson, x, y, z); else if(x > mid) updat(rson, x, y, z); else updat(lson, x, mid, z), updat(rson, mid + 1, y, z); sum[k] = sum[k << 1] + sum[k << 1 | 1]; } _L query(_RG s, _RG t, _RG k, _RG x, _RG y) { _RG mid = s + t >> 1; downt(s, t, k); if(s ^ t) downt(s, mid, k << 1), downt(mid + 1, t, k << 1 | 1); if(s == x && t == y) return sum[k]; if(y <= mid) return query(lson, x, y); if(x > mid) return query(rson, x, y); return query(lson, x, mid) + query(rson, mid + 1, y); } void solve() { for(_RG i = 1; i <= m; ++i) que[i] = (Q) {l[i], r[i], i}; std::sort(que + 1, que + 1 + m, [](const Q &u, const Q &v) {return u.l > v.l;}); steck[sec = 1] = n + 1; a[n + 1] = 1e9; for(_RG i = n; i; --i) { while(sec && a[steck[sec]] < a[i]) --sec; nex[i] = steck[sec]; steck[++sec] = i; } tim = 1; for(_RG i = n; i; --i) { updat(1, n + 1, 1, i + 1, nex[i], p2); updat(1, n + 1, 1, nex[i], nex[i], p1 - p2 - p2); while(tim <= m && que[tim].l == i) { ans[que[tim].id] += query(1, n + 1, 1, i, que[tim].r); ++tim; } if(tim > m) break; } } int main() { scanf("%d%d%d%d", &n, &m, &p1, &p2); for(_RG i = 1; i <= n; ++i) scanf("%d", a + i); for(_RG i = 1; i <= m; ++i) scanf("%d%d", l + i, r + i); solve(); memset(sum, 0LL, sizeof sum), memset(tag, 0LL, sizeof tag); for(_RG i = 1; i <= n / 2; ++i) std::swap(a[i], a[n - i + 1]); for(_RG i = 1; i <= m; ++i) l[i] = n - l[i] + 1, r[i] = n - r[i] + 1, std::swap(l[i], r[i]); solve(); for(_RG i = 1; i <= m; ++i) printf("%lld\n", ans[i]); return 0; }
枚举当前数为次小值,则可以直接为区间加上贡献,用线段树覆盖,
以上是关于[jzoj5073 GDOI2017第二轮模拟] 影魔的主要内容,如果未能解决你的问题,请参考以下文章
JZOJ4419GDOI2016模拟4.2hole(四~三维偏序问题)
[jzoj 6084] [GDOI2019模拟2019.3.25] 礼物 [luogu 4916] 魔力环 解题报告(莫比乌斯反演+生成函数)