题解: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;
}
posted @ 2026-06-06 12:47  Super_lollipop  阅读(6)  评论(0)    收藏  举报