HDU2665 Kth number 可持久化线段树

题目大意

题目连接

无修改区间第K大数

解题思路

第一次实现了主席树,其实还有两种做法,一种是划分树,WJMZBMR说它是时代的眼泪了,于是我也没想……
一种是线段树套平衡树……拜托没必要,这道题又没有修改……
所谓主席树,就是可持久化线段树,也就是说我们每插入了一个新的元素,就创造了一个新的结点,这样下去,线段树所有的历史版本我们就都能保存下来。
然后考虑一下线段树相减,两棵线段树相减就是每一个结点相减(权值线段树不解释),那么我们每一个结点更新一次,那么序列中每一个元素都对应了一个版本的线段树,也就是序列中所有的前缀的权值线段树,那么对于一个区间,通过前缀相减很快就能搞出来这个区间对应的线段树,然后如何询问这棵线段树的第K大值……嘛……不用解释

AC代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = (int)3e6 + 10;
const int maxn = (int)1e5;
int lson[N], rson[N], sum[N];
int Time[N], total;
int s[N], X[N];
void PushUp(int rt){
    sum[rt] = sum[lson[rt]] + sum[rson[rt]];
}
int build(int L, int R){
    int now = ++total;
    if (L == R){
        lson[now] = rson[now] = 0;
        sum[now] = 0;
        return now;
    }
    int m = (L + R) >> 1;
    lson[now] = build(L, m);
    rson[now] = build(m + 1, R);
    PushUp(now);
    return now;
}
int update(int rt, int pos, int L, int R){
    int now = ++total;
    if (L == R){
        sum[now] = sum[rt] + 1;
        lson[now] = rson[now] = 0;
        return now;
    }
    int m = (L + R) >> 1;
    if (pos <= m){
        lson[now] = update(lson[rt], pos, L, m);
        rson[now] = rson[rt];
    }else{
        rson[now] = update(rson[rt], pos, m+ 1, R);
        lson[now] = lson[rt];
    }
    PushUp(now);
    return now;
}
int ask(int rt1, int rt2, int k, int L, int R){
    if (L == R) return X[L];
    int number = sum[lson[rt2]] - sum[lson[rt1]];
    int m = (L + R) >> 1;
    if (number >= k)
        return ask(lson[rt1], lson[rt2], k, L, m);
    else
        return ask(rson[rt1], rson[rt2], k - number, m +1, R);
}
int main(){
    int cs; scanf("%d", &cs);
    int n, m;
    while(cs--){
        scanf("%d%d", &n, &m);
        int cnt = 0;
        for (int i = 1; i <= n; i++){
            scanf("%d", &s[i]);
            X[++cnt] = s[i];
        }
        sort(X + 1, X + 1 + cnt);
        int all = 1;
        for (int i = 2; i <= cnt; i++)
            if (X[i] != X[i - 1]) X[++all] = X[i];
        total = 0;
        Time[0] = build(1, all);
        for (int i = 1; i <= n; i++){
            int pos = lower_bound(X + 1, X + 1 + all, s[i]) - X;
            Time[i] = update(Time[i - 1], pos, 1, all);
        }
        while(m--){
            int L, R, k;
            scanf("%d%d%d", &L, &R, &k);
            printf("%d\n", ask(Time[L - 1], Time[R], k, 1, all));
        }
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值