码迷,mamicode.com
首页 > 其他好文 > 详细

主席树总结

时间:2020-02-03 20:49:01      阅读:70      评论:0      收藏:0      [点我收藏+]

标签:int   dig   tps   names   define   git   模板题   bit   主席树   

主席树,即可持久化线段树,它是在权值线段树的基础上,记录了每个历史版本,从而可以方便快速的处理一些区间问题,最经典的应用有查询区间第 k 小。

当然,如果我们直接每次修改开一棵线段树,空间复杂度太大,肯定不行。但我们可以发现每次修改都只会改变一个结点的值,对于线段树来说,也就是只会有左儿子或者右儿子的值被改变,所以我们将没有改变的一半直接用之前的版本就好了。

用图片来解释主席树还是比较直观的。

橙色为历史结点。

技术图片

技术图片

技术图片

技术图片

以上图片均出自 孤独·粲泽 大佬的 blog

代码

Luogu模板题

#include <bits/stdc++.h>
#define N 200005
using namespace std;

int gi() {
    int x = 0, f = 1; char c = getchar();
    for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

int n, m, tot;
int a[N], hs[N];
int cnt[N << 5], L[N << 5], R[N << 5], tr[N];

void modify(int lst, int &now, int l, int r, int k) {
    if (!now) now = ++tot;
    cnt[now] = cnt[lst] + 1;
    if (l == r) return;
    int mid = l + r >> 1;
    if (k <= mid) R[now] = R[lst], modify(L[lst], L[now], l, mid, k);
    else L[now] = L[lst], modify(R[lst], R[now], mid + 1, r, k);
}

int query(int u, int v, int l, int r, int k) {
    if (l == r) return l;
    int x = cnt[L[v]] - cnt[L[u]], mid = l + r >> 1;
    if (k <= x) return query(L[u], L[v], l, mid, k);
    else return query(R[u], R[v], mid + 1, r, k - x);
}

int main() {
    int l, r, k;
    n = gi(), m = gi();
    for (int i = 1; i <= n; ++i) a[i] = hs[i] = gi();
    sort(hs + 1, hs + 1 + n);
    int num = unique(hs + 1, hs + 1 + n) - hs - 1;
    for (int i = 1; i <= n; ++i) modify(tr[i - 1], tr[i], 1, num, lower_bound(hs + 1, hs + 1 + num, a[i]) - hs);
    for (int i = 1; i <= m; ++i) {
        l = gi(), r = gi(), k = gi();
        printf("%d\n", hs[query(tr[l - 1], tr[r], 1, num, k)]);
    }
    return 0;
}

主席树总结

标签:int   dig   tps   names   define   git   模板题   bit   主席树   

原文地址:https://www.cnblogs.com/hlw1/p/12257212.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!