主席树学习------可持久化权值线段树

洛谷 P3834

静态区间第k小

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

#define ll long long
const int maxn = 200005;
int n,q,tot,rt[maxn],a[maxn],b[maxn];
struct chairman_tree
{
    int son[2],siz;
} node[maxn*50];
void build(int &rt,int l,int r)   //建一颗空树
{
    rt = ++tot;                   //动态开点
    if(l==r) return;
    int mid = (l + r) >> 1;
    build(node[rt].son[0],l,mid);
    build(node[rt].son[1],mid+1,r);
}
void update(int &rt,int last,int l,int r,int val)   //插入节点,实际上是从上往下新建一条链
{
    node[rt=++tot] = node[last];            //复制上一个树的节点
    ++node[rt].siz;                         //修改大小
    if(l==r) return;
    int mid = (l + r) >> 1;
    if(val<=mid) update(node[rt].son[0],node[last].son[0],l,mid,val); //寻找继续修改的位置,是左子树还是右子树
    else update(node[rt].son[1],node[last].son[1],mid+1,r,val);
}
int query(int rt1,int rt2,int l,int r,int k)  //询问,利用前缀和思想,返回第k小的数的位置
{
    if(l==r) return l;
    int mid = (l + r) >> 1;
    if(node[node[rt2].son[0]].siz-node[node[rt1].son[0]].siz>=k)
        return query(node[rt1].son[0],node[rt2].son[0],l,mid,k);
    else return query(node[rt1].son[1],node[rt2].son[1],mid+1,r,k-node[node[rt2].son[0]].siz+node[node[rt1].son[0]].siz);

}

signed main()
{
    scanf("%d %d",&n,&q);
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);b[i]=a[i];
    }
    sort(b+1,b+1+n);
    int cnt = unique(b+1,b+1+n)-(b+1);  //离散化
    //build(rt[0],1,cnt);    //这一步可以不用
    for(int i=1;i<=n;++i)
        update(rt[i],rt[i-1],1,cnt,lower_bound(b+1,b+cnt+1,a[i])-b); //用离散化后的值更新
    int l,r,k;
    for(int i=1;i<=q;++i)
    {
        scanf("%d %d %d",&l,&r,&k);
        printf("%d\n",b[query(rt[l-1],rt[r],1,cnt,k)]); //输出原来的值
    }

    return 0;
}


posted @ 2022-02-16 11:08  HIVM  阅读(23)  评论(0编辑  收藏  举报