题解:P14301 [JOI2023 预选赛 R2] 日本沉没 2 / Japan Sinks 2
观察题目发现并不好直接维护,所以我们转换一下考虑维护一下他的轮廓,如图:

我们发现这个轮廓其实就是 \(\min(premax_i, sufmax_i)\) 我们暂且称他为 \(c_i\),同时我们称它本来的高度为 \(a_i\)。
我们观察一下他的性质:如果 \(c_i > a_i\) 说明即使有风吹过来他的海拔也不会变(因为他的轮廓比他高所以前面、后面会挡住),此时高度为 \(a_i\),反之,有风吹过就会降低海拔,此时高度就为 \(c_i\)。所以一个点在某一时刻时的高度就是 \(\min(c_i, a_i)\)。
考虑如何维护 \(c_i\):我们以西风举例,首先风是不可能吹过\(c_i\) 最高的那一块的,所以我们需要维护 \(c_i\) 最高的那一块的左端点和右端点。所以下面我们只考虑最高处左边的情况。
我们考虑一下前面的 \(c_i\) 的修改对后面的影响,如果 \(c_i\) 减少,后面的 \(premax_i\) 也可能会减少,导致后面的 \(c_i\) 减少(这里不明白的话可以去看一看第三个小样例有很好的启示作用)。而减少的右端点是什么呢?应该是他后面第一个能被风吹到的地方,即 \(c_i - a_i \leq 0\) 的地方。正确性显然:如果这个地方能被风吹到,那么他的 \(premax_i\) 不会因为前面的减小而减小,除非有风吹到他自己这里。
剩下的就好维护了,用线段树维护区间加,全局最大值的左右端点,区间 \(c_i - a_i\) 的最小值。
如何找第一个能被吹到的地方你可以写线段树上二分,总复杂度是 \(O(n\log n)\),因为我比较懒所以我直接写的二分,复杂度是 \(O(n \log^2 n)\)。
#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstring>
#include <cstdint>
using namespace std;
using i64 = long long;
#define int i64
const i64 INF = 1e18;
using pii = pair<int, int>;
#define L(i, a, b) for(int i = (a); i <= (b); ++i)
#define R(i, a, b) for(int i = (a); i >= (b); --i)
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define file(s) freopen(#s ".in", "r", stdin), freopen(#s ".out", "w", stdout)
const int N = 5e5 + 20;
int n, q, a[N], pre[N], suf[N];
struct Segment{
#define ls u << 1
#define rs u << 1 | 1
#define mid ((l + r) >> 1)
int tag[N << 2], ql[N << 2], qr[N << 2], mxn[N << 2], _mxn[N << 2];
inline void pushup(int u){
mxn[u] = max(mxn[ls], mxn[rs]);
_mxn[u] = min(_mxn[ls], _mxn[rs]);
if(mxn[ls] == mxn[rs]){
ql[u] = min(ql[ls], ql[rs]);
qr[u] = max(qr[ls], qr[rs]);
}
else if(mxn[ls] > mxn[rs]) ql[u] = ql[ls], qr[u] = qr[ls];
else ql[u] = ql[rs], qr[u] = qr[rs];
}
inline void build(int u = 1, int l = 1, int r = n){
if(l == r) return mxn[u] = min(pre[l], suf[l]), ql[u] = l, qr[u] = r, _mxn[u] = mxn[u] - a[l], void();
build(ls, l, mid), build(rs, mid + 1, r);
pushup(u);
}
inline void pushdown(int u){
if(tag[u]){
mxn[ls] += tag[u], mxn[rs] += tag[u];
_mxn[ls] += tag[u], _mxn[rs] += tag[u];
tag[ls] += tag[u], tag[rs] += tag[u];
tag[u] = 0;
}
}
inline void add(int L, int R, int v, int u = 1, int l = 1, int r = n){
if(L <= l && r <= R) return mxn[u] += v, tag[u] += v, _mxn[u] += v, void();
pushdown(u);
if(L <= mid) add(L, R, v, ls, l, mid);
if(R >= mid + 1) add(L, R, v, rs, mid + 1, r);
pushup(u);
}
inline int qrym(int pos, int u = 1, int l = 1, int r = n){
if(l == r) return mxn[u];
pushdown(u);
if(pos <= mid) return qrym(pos, ls, l, mid);
else return qrym(pos, rs, mid + 1, r);
}
inline int rm(int L, int R, int u = 1, int l = 1, int r = n){
if(L <= l && r <= R) return mxn[u];
int res = 0; pushdown(u);
if(L <= mid) res = max(res, rm(L, R, ls, l, mid));
if(R >= mid + 1) res = max(res, rm(L, R, rs, mid + 1, r));
return res;
}
inline int _rm(int L, int R, int u = 1, int l = 1, int r = n){
if(L <= l && r <= R) return _mxn[u];
int res = INF; pushdown(u);
if(L <= mid) res = min(res, _rm(L, R, ls, l, mid));
if(R >= mid + 1) res = min(res, _rm(L, R, rs, mid + 1, r));
return res;
}
#undef mid
#undef ls
#undef rs
}t;
// range add / pos query
int find(int L, int R){
int res = 0;
int _L = L;
while(L <= R){
int mid = (L + R) >> 1;
if(t._rm(_L, mid) <= 0) R = mid - 1, res = mid;
else L = mid + 1;
}
return res;
}
int find_(int L, int R){
int res = 0;
int _R = R;
while(L <= R){
int mid = (L + R) >> 1;
if(t._rm(mid, _R) <= 0) L = mid + 1, res = mid;
else R = mid - 1;
}
return res;
}
signed main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> q; for(int i = 1; i <= n; ++i) cin >> a[i], pre[i] = max(pre[i - 1], a[i]);
for(int i = n; i >= 1; --i) suf[i] = max(suf[i + 1], a[i]);
t.build();
for(int i = 1; i <= q; ++i){
int op, x; cin >> op >> x;
if(op == 1){
if(x >= t.qr[1]) t.add(1, t.qr[1], -1);
else t.add(1, find(x + 1, t.qr[1]) - 1, -1);
}
else if(op == 2){
x = n - x + 1;
if(x <= t.ql[1]) t.add(t.ql[1], n, -1);
else t.add(find_(t.ql[1], x - 1) + 1, n, -1);
}
else cout << min(a[x], t.qrym(x)) << '\n';
}
return 0;
}

浙公网安备 33010602011771号