Luogu P3834 【模板】可持久化线段树 1(主席树)

就是板子、、、

节点中维护的值,就是1-i之间这个区间内出现了数的次数(权值线段树?雾)。然后当我们查询的时候,就是利用到了前缀和的思想,拿左端点那棵树和右端点一减~

更新的时候需要新开的点就开,不需要的就连到原来的点上去,相当于更新一条链。这样复杂度是nlogn的。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define R register int
#define ls (tr<<1)
#define rs (tr<<1|1)
using namespace std;
inline int g() {
    R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
    do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
}
const int N=200010;
int n,m,q,tot,T;
int a[N],b[N];
int t[N],sum[N<<5],L[N<<5],Rr[N<<5];
inline int build(int l,int r) {
    R tr=++tot,md=(l+r)>>1; sum[tr]=0;
    if(l<r) L[tr]=build(l,md),Rr[tr]=build(md+1,r);
    return tr;
}
inline int upd(int pre,int l,int r,int vl) {
    R tr=++tot; R md=(l+r)>>1;
    L[tr]=L[pre],Rr[tr]=Rr[pre],sum[tr]=sum[pre]+1;
    if(l<r) {
        if(vl<md+1) L[tr]=upd(L[pre],l,md,vl);
        else Rr[tr]=upd(Rr[pre],md+1,r,vl);
    }
    return tr;
}
inline int query(int lst,int tr,int l,int r,int k) {
    if(l>=r) return l; R md=(l+r)>>1;
    R x=sum[L[tr]]-sum[L[lst]];
    if(x>=k) return query(L[lst],L[tr],l,md,k);
    else return query(Rr[lst],Rr[tr],md+1,r,k-x);
}
signed main() {
    n=g(),q=g();
    for(R i=1;i<=n;++i) a[i]=b[i]=g();
    sort(b+1,b+n+1);
    R m=unique(b+1,b+n+1)-b-1;
    t[0]=build(1,m);
    for(R i=1;i<=n;++i) {
        a[i]=lower_bound(b+1,b+m+1,a[i])-b;
        t[i]=upd(t[i-1],1,m,a[i]);
    }
    while(q--) {
        R x=g(),y=g(),z=g(); R p=query(t[x-1],t[y],1,m,z);
        printf("%d
",b[p]);
    }
}

好想大佬们都把根作为实参转进去。。我的码风有待提高。。QAQ


2019.04.17

原文地址:https://www.cnblogs.com/Jackpei/p/10727096.html