【学习笔记】主席树

主席树,又称可持久化线段树,顾名思义,就是一种可以访问历史版本的数据结构.

所谓访问历史版本,就是在每次修改了线段树以后,并不把原先的树破坏掉,而是在原来的树上新加入一些节点,使得这些节点与原来的线段树的一部分构成了修改后的线段树.

考虑线段树的修改操作。由于每次修改最多只会修改 l o g n 个节点,而对于其他的节点,新旧线段树都是可以共用的,所以,我们只需要将这些对于这些修改过的节点新建一些节点,并且保留原有的父子关系,来构成新的线段树就可以了。这样,最终所有新加入的节点个数就是 m l o g n 其中 m 是操作的次数,并且,单次操作的复杂度还是 l o g n .

具体的,如果要求区间第 k 小。首先离散,按离散后的权值建立一棵权值线段树。我们可以将每一个数看作一次添加操作, A i 就是将权值为 A i 的数加入到线段树中,那么,按照这样的法则建立的主席树, [ L , R ] 区间就相当于是第 R 次操作与第 L 1 次操作之间的区间,要求区间第 k 小,只要比这个数小的数有 k 1 个,又由于线段树是按照权值构造的,所以可以很方便的求出两个历史版本之间比某个数小的数的个数的差值,这个差值,就是在区间内比这个数小的数的个数,用类似BST的方法就可以查询区间第 k 大。

poj2104就是上面的题。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxm 2000006
#define maxn 100006
using namespace std;
inline char nc(){
    static char buf[100000],*i=buf,*j=buf;
    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline int _read(){
    char ch=nc();int sum=0,p=1;
    while(ch!='-'&&!(ch>='0'&&ch<='9'))ch=nc();
    if(ch=='-')p=-1,ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum*p;
}
int n,m,T,a[maxn],b[maxn];
struct node{
    node *l,*r;
    int L,R,s;
    node(int x=0,int y=0) {L=x;R=y;s=0;}
}nil,base[maxm];
typedef node* p_node;
p_node null=&nil,len=base,rot[maxn];
p_node newnode(int l,int r){*len=node(l,r);len->l=len->r=null;return len++;}
p_node build(int l,int r){
    p_node x=newnode(l,r);
    if(l>=r)return x;
    int mid=l+r>>1;
    x->l=build(l,mid);x->r=build(mid+1,r);
    return x;
}
p_node update(p_node lst,int k){
    p_node x=newnode(lst->L,lst->R);
    x->s=lst->s;x->l=lst->l;x->r=lst->r;
    if(x->L==x->R){x->s++;return x;}
    int mid=x->L+x->R>>1;
    if(k<=mid)x->l=update(x->l,k);
         else x->r=update(x->r,k);
    x->s=x->l->s+x->r->s;
    return x;
}
int query(p_node l,p_node r,int k){
    if(l->L==l->R)return b[l->L];
    int tem=r->l->s-l->l->s;
    if(k<=tem)return query(l->l,r->l,k);
         else return query(l->r,r->r,k-tem);
}
int main(){
    freopen("chariman.in","r",stdin);
    freopen("chariman.out","w",stdout);
    null->l=null;null->r=null;
    n=_read();T=_read();
    for(int i=1;i<=n;i++)b[i]=a[i]=_read();
    sort(b+1,b+1+n);
    m=unique(b+1,b+1+n)-b-1;
    rot[0]=build(1,m);
    for(int i=1;i<=n;i++)rot[i]=update(rot[i-1],lower_bound(b+1,b+1+m,a[i])-b);
    while(T--){
        int l=_read(),r=_read(),k=_read();
        printf("%d\n",query(rot[l-1],rot[r],k));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/fyoier/article/details/80452603
今日推荐