Luogu P14255 列车(train) 题解 [ 蓝 ] [ 线段树 ] [ 二维平面转化 ]

列车

感觉自己 DS 学傻了,赛时瞄了一眼口胡了个神秘科技做法并鉴定为不可做题,赛后一说做法是普通线段树秒会。

把限制画在二维平面上是显然的,令横轴为 \(i\),纵轴为 \(j\),则初始有用的部分形成一个倒三角。

考虑分别刻画两种操作:

  • 查询操作相当于在平面上查询点 \((x, y)\) 左上角的最小值。
  • 修改操作相当于把 \((x, y)\) 右下角的点全部设为极大值。

观察修改操作,发现被设为极大值的部分一定是楼梯形状的。观察查询操作,发现当 \(i\) 确定时,\(j\) 越小 \(p\) 越小,因此维护二维平面是没有必要的,对于每个 \(i\) 维护最小的 \(\bm j\) 即可。这样就把二维平面拍成了一维序列来维护。

考虑用线段树来维护这个序列:

  • 修改操作:用 set 维护楼梯的形状,修改之前先在 set暴力查询修改的是哪部分楼梯,将需要修改的部分在线段树上修改即可。因为每次修改最多只会增加一个段,所以 set 上暴力修改均摊复杂度是正确的。线段树具体需要维护的是区间赋值、区间求最小值的操作。
  • 查询操作:分为两部分。第一部分是在楼梯内部的区域,这部分直接在线段树上查询。第二部分是在楼梯外部的区域,此时 \(i\) 取最大,\(j\) 取最小一定最优。特判掉即可。

时间复杂度 \(O(n\log n)\)

#include <bits/stdc++.h>
#define lc (p << 1)
#define rc ((p << 1) | 1)
#define fi first
#define se second
using namespace std;
typedef long long ll;
using pi = pair<int, int>;
const int N = 200005, inf = 0x3f3f3f3f;
int n, m, pos[N];
set<pi> s1, s2;
struct Node{
    int l, r;
    int mn, tag;
};
struct Segtree{
    Node tr[4 * N];
    void modify(int p, int v)
    {
        tr[p].mn = v - pos[tr[p].r];
        tr[p].tag = v;
    }
    void pushup(int p)
    {
        tr[p].mn = min(tr[lc].mn, tr[rc].mn);
    }
    void pushdown(int p)
    {
        if(tr[p].tag)
        {
            modify(lc, tr[p].tag);
            modify(rc, tr[p].tag);
        }
        tr[p].tag = 0;
    }
    void build(int p, int ln, int rn)
    {
        tr[p] = {ln, rn, 0, 0};
        if(ln == rn) return;
        int mid = (ln + rn) >> 1;
        build(lc, ln, mid);
        build(rc, mid + 1, rn);
        pushup(p);
    }
    void update(int p, int ln, int rn, int v)
    {
        if(ln <= tr[p].l && tr[p].r <= rn)
        {
            modify(p, v);
            return;
        }
        int mid = (tr[p].l + tr[p].r) >> 1;
        pushdown(p);
        if(ln <= mid) update(lc, ln, rn, v);
        if(rn >= mid + 1) update(rc, ln, rn, v);
        pushup(p);
    }
    int query(int p, int ln, int rn)
    {
        if(ln <= tr[p].l && tr[p].r <= rn) return tr[p].mn;
        pushdown(p);
        int mid = (tr[p].l + tr[p].r) >> 1;
        if(rn <= mid) return query(lc, ln, rn);
        if(ln >= mid + 1) return query(rc, ln, rn);
        return min(query(lc, ln, rn), query(rc, ln, rn));
    }
}tr1;
void OP1(int x, int y)
{
    int now = x;
    auto it = s1.upper_bound({x, inf});
    --it;
    int nowh = (*it).se;
    if(y <= nowh) return;
    it = s1.lower_bound({x, 0});
    int rn = -1;
    while(1)
    {
        if(it == s1.end())
        {
            rn = n;
            break;
        }
        rn = (*it).fi - 1;
        int tx = (*it).fi, ty = (*it).se;
        if(ty > y) break;
        ++it;
        s1.erase({tx, ty});
        s2.erase({ty, tx});
    }
    s1.insert({x, y});
    s2.insert({y, x});
    tr1.update(1, x, rn, pos[y]);
}
void OP2(int x, int y)
{
    int res = inf;
    auto it = s2.upper_bound({y, 0});
    int rn = 1;
    if(it != s2.begin())
    {
        rn = min(x + 1, (*it).se);
        if(1 <= rn - 1) res = min(res, pos[y] - pos[rn - 1]);
    }
    if(rn <= x) res = min(res, tr1.query(1, rn, x));
    if(res >= inf) cout << "-1\n";
    else cout << res << "\n";
}
void solve()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> pos[i];
    pos[n + 1] = 2 * inf;
    s1.clear(); s2.clear();
    s1.insert({0, 0});
    s2.insert({0, 0});
    for(int i = 1; i <= n; i++)
    {
        s1.insert({i, i});
        s2.insert({i, i});
    }
    tr1.build(1, 1, n);
    while(m--)
    {
        int op, x, y;
        cin >> op >> x >> y;
        if(op == 1) OP1(x, y + 1);
        else OP2(x, y);
    }
}
int main()
{
    // freopen("train.in", "r", stdin);
    // freopen("train.out", "w", stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while(t--) solve();
    return 0;
}
posted @ 2025-10-19 12:27  KS_Fszha  阅读(10)  评论(0)    收藏  举报