CF484E 题解

很好的数据结构题,加深了蒟蒻对于可持久化线段树的理解。

题意

给定一个序列 {an} ( 1n105,1ai109 ),有 m ( 1m105 ) 个询问,每次询问给出 l,r,k,表示询问区间 [l,r] 里长度为 k 的子区间的最小值最大是多少。

题解

看到最小值最大,考虑二分答案,根据 middle 那道题的套路,将 mid 的数在原序列中赋值成 1<mid 的数在原序列中赋值成 0。于是问题转化成了:判断区间 [l,r] 中有没有一个长度为 k 的子区间,满足这个子区间内的数全为 1

这个东西不太能离线做,因为要二分。考虑在线回答,这就显然要用到可持久化线段树了。

这个时候就很容易被套路给误导了,去想该如何主席树求解这个问题,像 HH 的项链 那道题一样维护 pre,但发现是要求一个区间里面的东西,这不太好维护。实际上,可持久化线段树的不同版本不一定只建立在序列的下标上,也可以建立在权值上,这道题中,第 i 个版本的线段树维护 i 的数的最长连续子段即可,也就是类似 小白逛公园 那道题一样。二分到 mid 时,查询第 mid 棵线段树中区间 [l,r] 的最长连续子段长度是否 k 即可。

代码很好写,时间复杂度 O(nlog2n),code:

#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define per(i, r, l) for (int i = r; i >= l; --i)
#define re(i, n) for (int i = 0; i < n; ++i)
#define pe(i, n) for (int i = n - 1; i >= 0; --i)
#define bg begin()
#define ed end()
#define alls(a) a.bg, a.ed
using namespace std;
using i64 = long long;
const int N = 1E5 + 5;
int n, m, a[N], tot, root[N];
struct segt {
    int ls[N << 5], rs[N << 5];
    struct node {
        int lb = 0, rb = 0;
        int len = 0, mx = 0;
    } t[N << 5];
    friend node operator+(node a, node b) {
        node c;
        c.len = a.len + b.len;
        c.lb = (a.len == a.lb ? a.len + b.lb : a.lb);
        c.rb = (b.len == b.rb ? b.len + a.rb : b.rb);
        c.mx = max({a.mx, b.mx, a.rb + b.lb});
        return c;
    }
    void pull(int x) {t[x] = t[ls[x]] + t[rs[x]];}
    void build(int &x, int l, int r) {
        x = ++tot;
        if (l == r) {t[x].len = 1; return ;}
        int mid = (l + r) / 2;
        build(ls[x], l, mid);
        build(rs[x], mid + 1, r);
        pull(x);
    }
    void insert(int &p, int q, int l, int r, int pos) {
        t[p = ++tot] = t[q]; ls[p] = ls[q]; rs[p] = rs[q];
        if (l == r) {t[p].lb = t[p].rb = t[p].mx = 1; return ;}
        int mid = (l + r) / 2;
        if (pos <= mid) insert(ls[p], ls[q], l, mid, pos);
        else insert(rs[p], rs[q], mid + 1, r, pos);
        pull(p);
    }
    node query(int x, int l, int r, int L, int R) {
        if (r < L || l > R || !x) return node {};
        if (l >= L && r <= R) return t[x];
        int mid = (l + r) / 2;
        return query(ls[x], l, mid, L, R) + query(rs[x], mid + 1, r, L, R);
    }
    int query(int v, int L, int R) {return query(v, 1, n, L, R).mx;}
} t;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n; rep(i, 1, n) cin >> a[i];
    vector<array<int, 2>> v(n + 1);
    rep(i, 1, n) v[i] = {a[i], i};
    sort(v.bg + 1, v.ed);
    t.build(root[n + 1], 1, n);
    per(i, n, 1) t.insert(root[i], root[i + 1], 1, n, v[i][1]);
    cin >> m; while (m--) {
        int l, r, k; cin >> l >> r >> k;
        int L = 1, R = n;
        while (L < R) {
            int mid = (L + R + 1) / 2;
            if (t.query(root[mid], l, r) >= k) L = mid;
            else R = mid - 1;
        }
        cout << v[L][0] << '\n';
    }
}

作者:CTHOOH

出处:https://www.cnblogs.com/CTHOOH/p/18259150

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   CTHOOH  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示