【数据结构】可持久化线段树&Trie总结

可持久化:

可以访问历史版本的“升级版”的数据结构,利用访问历史版本的性质,可以做到许多在区间上的操作。

可持久化线段树:

例题:
cqoi2111:区间第k大

给定一个长度为n的序列,m个询问,每个询问的形式为:l,r,k表示在[l,r]间中的第k大元素.
(n<=100000, m<=100000,1<=l<=r<=n, 1<=k<=r-l+1)

如果用线段树来储存某个区间的第i大,显然是很不好实现的。因此,我们换一个思路,我们将线段树建在值域上(需要离散化),储存某个区间存在多少个数。
每插入一个新的值,那么这个值会影响线段树上的哪些节点?
《【数据结构】可持久化线段树&Trie总结》
《【数据结构】可持久化线段树&Trie总结》
这样一来,我们就做到了保存历史版本。
具体实现,我们可以再插入新节点时,插入位置以外的半个区间的指针指向上一个版本,继续走当前这个区间,直到抵达叶节点为止。
对于这道题,求解的过程很类似于在平衡树上找第k大
如果左区间的数的个数>k,走左树
反之,走右树,并将 k-左区间的数的个数

附上我的(丑)模板

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
struct node{
    int cnt;
    node *ch[2];
}Seg[MAXN*20],*root[MAXN],*ncnt=Seg;
int a[MAXN],b[MAXN],bn,n,m;
void init(node *p){
    p->cnt=0;
    p->ch[0]=p->ch[1]=NULL;
}
void build(node *&p,int l,int r){
    p=++ncnt;
    init(p);
    if(l+1>=r)
        return ;
    int mid=(l+r)>>1;
    build(p->ch[0],l,mid);
    build(p->ch[1],mid,r);
}
void insert(node *&x,node *&y,int val,int l,int r){
    x=++ncnt;
    *x=*y;
    x->cnt++;
    if(l+1>=r)
        return ;
    int mid=(l+r)>>1;
    if(val<mid)
        insert(x->ch[0],y->ch[0],val,l,mid);
    else
        insert(x->ch[1],y->ch[1],val,mid,r);
}
int Pos(int x){
    return lower_bound(b+1,b+1+n,x)-b;
}
void prepare(){
    SF("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        SF("%d",&a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+1+n);
    bn=unique(b+1,b+1+n)-b;
    build(root[0],1,bn);
    for(int i=1;i<=n;i++)
        insert(root[i],root[i-1],Pos(a[i]),1,bn);
}
int find(node *x,node *y,int k,int l,int r){
    if(l+1>=r)
        return l;
    int mid=(l+r)>>1;
    int ts=(x->ch[0]->cnt)-(y->ch[0]->cnt);
    if(ts>=k)
        find(x->ch[0],y->ch[0],k,l,mid);
    else
        find(x->ch[1],y->ch[1],k-ts,mid,r);
}
void solve(){
    int l,r,k;
    for(int i=1;i<=m;i++){
        SF("%d%d%d",&l,&r,&k);
        PF("%d\n",b[find(root[r],root[l-1],k,1,bn)]);
    }
}
int main(){
    prepare();
    solve();
}
//CQOI2011

另外一道简单例题:
SPOJ-COT

简单地说,可持久化地过程,就是对于每个点,均插入logn个节点来储存它所在的历史版本的信息。

可持久化Trie

例题:
HDU4757:Tree
实质上和可持久化线段树是一样的,
为了保存历史版本,我们对于每个点,都插入logn个节点
具体操作与可持久化线段树也差不多。
求解的时候,用贪心的方式即可。(即)

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<cstring>
#define SF scanf
#define PF printf
#define MAXN 100010
using namespace std;
struct node{
    int cnt;
    int ch[2];
}Seg[MAXN*20];
int ncnt,root[MAXN];
int a[MAXN],n,m,u,v;
const int len=18;
int fa[MAXN][20],deep[MAXN];
vector<int> r[MAXN];
void insert(int &x,int &y,int val,int len1){
    x=++ncnt;
    Seg[x]=Seg[y];
    Seg[x].cnt++;
    if(len1<0)
        return ;
    int next=(val>>len1)&1;
    insert(Seg[x].ch[next],Seg[y].ch[next],val,len1-1);
}
void dfs(int x,int f,int dep){
    insert(root[x],root[f],a[x],len);
    fa[x][0]=f;
    deep[x]=dep;
    for(int i=1;i<20;i++)
        fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int i=0;i<r[x].size();i++){
        if(r[x][i]==f)
            continue;
        dfs(r[x][i],x,dep+1);
    }
}
void prepare(){
    for(int i=1;i<=n;i++)
        SF("%d",&a[i]);
    for(int i=1;i<n;i++){
        SF("%d%d",&u,&v);
        r[u].push_back(v);
        r[v].push_back(u);
    }
    dfs(1,0,1);
}
int find(int x,int y,int xx,int yy,int len1,int val){
    if(len1<0)
        return 0;
    int res=0,x1,y1,xx1,yy1;
    int k1=(val>>len1)&1;
    k1^=1;
    x1=Seg[x].ch[k1];
    y1=Seg[y].ch[k1];
    xx1=Seg[xx].ch[k1];
    yy1=Seg[yy].ch[k1];
    if(Seg[x1].cnt+Seg[y1].cnt>Seg[xx1].cnt+Seg[yy1].cnt){
        res=find(x1,y1,xx1,yy1,len1-1,val);
        if(len1!=len)
            res+=(1<<len1);
    }
    else{
        x1=Seg[x].ch[k1^1];
        y1=Seg[y].ch[k1^1];
        xx1=Seg[xx].ch[k1^1];
        yy1=Seg[yy].ch[k1^1];
        res=find(x1,y1,xx1,yy1,len1-1,val);
    }
    return res;
}
int lca(int x,int y){
    if(deep[x]<deep[y])
        swap(x,y);
    for(int i=19;i>=0;i--)
        if(deep[fa[x][i]]>=deep[y])
            x=fa[x][i];
    if(x==y)
        return x;
    for(int i=19;i>=0;i--)
        if(fa[x][i]!=fa[y][i]){
            x=fa[x][i];
            y=fa[y][i];
        }
    return fa[x][0];
}
void solve(){
    int val;
    for(int i=1;i<=m;i++){
        SF("%d%d%d",&u,&v,&val);
        int lcax=lca(u,v);
        PF("%d\n",find(root[u],root[v],root[lcax],root[fa[lcax][0]],len,val));
    }
}
void init(){
    ncnt=0;
    memset(Seg,0,sizeof Seg);
    memset(root,0,sizeof root);
    memset(fa,0,sizeof fa);
    memset(deep,0,sizeof deep);
    memset(a,0,sizeof a);
    for(int i=1;i<=n;i++)
        r[i].clear();

}
int main(){
    while(SF("%d%d",&n,&m)!=EOF){
        init();
        prepare();
        solve();
    }
}
    原文作者:Trie树
    原文地址: https://blog.csdn.net/qq_34454069/article/details/78603117
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞