P3834 可持久化线段树(主席树)入门

#include <bits/stdc++.h>

using namespace std;
#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define p_queue priority_queue

const int MAXN = 200005;
int n , m;
int size, len;
int a[MAXN], b[MAXN];
int lc[MAXN<<5], rc[MAXN<<5], sum[MAXN<<5], root[MAXN];
//lc某点左儿子编号,rc某点右儿子编号,sum某点所维护的值,root用于存储若干线段树根节点的编号
void build(int& rt, int l , int r) //采用引用,同时给左右儿子编号,&rt相当于lc[rt],rt变换,lc[rt]也会变换
{
    rt = ++ size; //用于节点编号,和线段树有所不同,不能根据公示得到左右儿子,需要动态编号
    sum[rt] = 0;
    if(l == r)
        return ;
    int mid = (l+r) >> 1;
    build(lc[rt], l , mid); //左儿子
    build(rc[rt],mid+1,r); //右儿子
    //如果这种不理解,下面的build也是可以的
}
/*
int build(int l, int r)
{
    ++ size;
    sum[size] = 0;
    if(l == r)
        return 0;
    int mid = (l+r) >> 1;
    lc[size] = build(l , mid);
    rc[size] = build(mid+1,r);
    return size;
}
*/
int update(int pre , int l , int r, int temp)
{
    int now = size ++; //新建一个节点
    //先将新建节点的左右儿子,指向上一颗线段树的同位置的左右儿子
    lc[now] = lc[pre], rc[now] = rc[pre], sum[now] = sum[pre] + 1;
    if(l == r) return now;
    int mid = (l + r) >> 1;
    //如果新建节点的左儿子需要更新则更新左儿子,让当前新建节点的指向改变,每次要么只更新左或者右,另外一个儿子指向上一颗树的同位置
    if(mid >= temp)
        lc[now] = update(lc[now],l,mid,temp);
    else
        rc[now] = update(rc[now],mid+1,r,temp);
    return now;
}
int query(int u , int v, int l , int r, int k)
{
    //两颗线段树做差之后,就相当于是[l-1,r]区间构成的线段树了,就可以转换为权值线段树Kth操作了
    int mid = (l + r) >> 1,  x = sum[lc[v]] - sum[lc[u]];
    if(l == r) return l;
    if(x >= k)
        return query(lc[u],lc[v],l,mid,k);
    else
        return query(rc[u],rc[v],mid+1,r,k-x);
}
int main(void)
{
    scanf("%d %d",&n , &m);
    for(int i = 1 ; i <= n ; i ++)
        scanf("%d",&a[i]), b[i] = a[i];
    sort(b+1,b+1+n);
    len = unique(b+1,b+1+n)-b-1;
    build(root[0],1,len); //root[0] = 1,空树根节点编号是1
    for(int i = 1 ; i <= n ; i ++)
    {
        int temp = lower_bound(b+1,b+1+len,a[i])-b;
        root[i] = update(root[i-1],1,len,temp);
    }
    for(int i = 1 ; i <= m ; i ++)
    {
        int l , r, k;
        scanf("%d %d %d",&l, &r, &k);
        printf("%d\n",b[query(root[l-1],root[r],1,len,k)]);
    }

}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值