【重码数据结构】主席树(可持久化线段树)

例题:https://www.luogu.org/problemnew/show/P3834

主席树用于查询每个历史版本。

这个题代码如下

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=200001;
int n,m,tot;
ll a[maxn],b[maxn];
struct stree{
    int lc,rc;
    ll sum;
    #define lc(x) tree[x].lc
    #define rc(x) tree[x].rc
    #define s(x) tree[x].sum
}tree[maxn<<5];
int root[maxn];
inline int build(int l,int r){
    int p=++tot;
    if(l==r) return p;
    int mid=(l+r)>>1;
    lc(p)=build(l,mid);
    rc(p)=build(mid+1,r);
    return p;
}
inline int insert(int now,int l,int r,int x){
    int p=++tot;
    tree[p]=tree[now];
    s(p)++;
    if(l==r)return p;
    int mid=(l+r)>>1;
    if(x<=mid) lc(p)=insert(lc(now),l,mid,x);
    else rc(p)=insert(rc(now),mid+1,r,x);
    return p;
}
inline int ask(int p,int q,int l,int r,int k){//在p,q两个时间节点上,在[l,r]的范围内的数的个数
    if(l==r) return l;
    int mid=(l+r)>>1;
    int lcnt=s(lc(q))-s(lc(p));
    if(k<=lcnt) return ask(lc(p),lc(q),l,mid,k);//在两个节点的左儿子中查找。
    else return ask(rc(p),rc(q),mid+1,r,k-lcnt);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]),b[i]=a[i];
    sort(b+1,b+n+1);
    int cnt=unique(b+1,b+n+1)-b-1;
    root[0]=build(1,cnt);
    for(int i=1;i<=n;i++){
        a[i]=lower_bound(b+1,b+cnt+1,a[i])-b;
        root[i]=insert(root[i-1],1,cnt,a[i]);
    }
    while(m--){
        int l,r,k;
        scanf("%d%d%d",&l,&r,&k);
        printf("%lld\n",b[ask(root[l-1],root[r],1,cnt,k)]);
    }
    system("pause");
    return 0;
}

这个题的历史版本是插入第 i 个数之前的。

在 [ l , r ] 值域内的元素个数。

猜你喜欢

转载自www.cnblogs.com/ChrisKKK/p/11025298.html