zoukankan      html  css  js  c++  java
  • [学习笔记]可持久化数据结构——数组、并查集、平衡树、Trie树

    可持久化:支持查询历史版本和在历史版本上修改

    可持久化数组

    主席树做即可。

    【模板】可持久化数组(可持久化线段树/平衡树)

    可持久化并查集

    可持久化并查集

    主席树做即可。

    要按秩合并。(路径压缩每次建logn条链,会卡爆空间MLE)

    主席树节点,维护father(是一个真实下标),维护dep(集合的最大深度),

    一个关键函数是query,找到代表实际位置为pos的节点的编号

    对于一个版本,

    合并:先找到这个两个位置的集合的根节点。

    不在同一个集合里的话,就合并。

    合并的时候,新建一条链,并且更新father,dep还是原来节点的dep

    如果和连向的father的dep相同的话,那就把father的点的dep++,象征这个新连上的集合深度是最深深度。

    (++deep的时候,可以不建立新节点。因为只是影响一些按秩合并效率,但是基本没有影响)

    (upda:2019.3.5 不会影响的。因为是对新节点的deep++,和之前版本没有任何关系)

    查询:直接查询即可。

     

    【模板】可持久化并查集

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define numb (ch^'0')
    #define mid ((l+r)>>1)
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=1e5+5;
    struct node{
        int ls,rs;
        int fa,dep;
    }t[N*40];
    int tot;
    int n,m;
    int las;
    int rt[2*N];
    void build(int &x,int l,int r){
        x=++tot;
        if(l==r) {
            t[x].fa=l,t[x].dep=1;
            return ;
        }
        build(t[x].ls,l,mid);
        build(t[x].rs,mid+1,r);
    }
    int query(int x,int l,int r,int to){
        if(l==r) return x;
        if(to<=mid) return query(t[x].ls,l,mid,to);
        else return query(t[x].rs,mid+1,r,to);
    }
    void merge(int &x,int y,int l,int r,int to,int ff){
        x=++tot;
        t[x].ls=t[y].ls;t[x].rs=t[y].rs;
        if(l==r) {
            t[x].fa=ff,t[x].dep=t[y].dep;return;
        }
        if(to<=mid) merge(t[x].ls,t[y].ls,l,mid,to,ff);
        else merge(t[x].rs,t[y].rs,mid+1,r,to,ff);
    }
    int find(int o,int to){
    //    cout<<" o "<<o<<" to "<<to<<endl;
        int now=query(rt[o],1,n,to);
        if(t[now].fa==to) return now;
        return find(o,t[now].fa);
    }
    int main(){
        scanf("%d%d",&n,&m);
        build(rt[0],1,n);
    //    cout<<" tot tot tot "<<tot<<endl;
    //        for(reg i=1;i<=tot;++i){
    //            cout<<i<<" : "<<t[i].fa<<" "<<t[i].dep<<endl;
    //        }
        int op,k,x,y;las=0;
        int o=0;
        while(m--){
            rd(op);
            if(op==1){
                ++o;
                rt[o]=rt[las];
                rd(x);rd(y);
                x=find(las,x);
                y=find(las,y);
                if(t[x].fa!=t[y].fa){
                    if(t[x].dep>t[y].dep) swap(x,y);
                    merge(rt[o],rt[las],1,n,t[x].fa,t[y].fa);
                    if(t[x].dep==t[y].dep) {
                    //    cout<<" dep equal "<<t[y].fa<<endl;
                        int lp=query(rt[o],1,n,t[y].fa);
                    //    cout<<" lplplp "<<lp<<endl;
                        t[lp].dep++;
                    }
                }
                las=o;
            }else if(op==2){
                ++o;
                rd(k);
                rt[o]=rt[k];
                las=k;
            }else{
                ++o;
                //cout<<" las "<<las<<endl;
                rt[o]=rt[las];
                rd(x);rd(y);
                //cout<<" x "<<" y "<<x<<" "<<y<<endl;
                x=find(las,x);
                y=find(las,y);
                //cout<<" xx "<<" yy "<<x<<" "<<y<<endl;
                if(t[x].fa==t[y].fa){
                    puts("1");
                }else puts("0");
                las=o;
            }
    //        cout<<" tot tot tot "<<tot<<endl;
    //        for(reg i=1;i<=tot;++i){
    //            cout<<i<<" : "<<t[i].fa<<" "<<t[i].dep<<endl;
    //        }
        }
        return 0;
    }
    
    }
    int main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/11/23 7:48:57
    */
    可持久化并查集

    不能在历史版本上更改的可持久化并查集。(也就是,历史版本形成的树是一条链)

     (可持久化并茶几O(logn): (NOI2018D1T1) 每个点记录每时每刻在哪个集合里 用vector记录pair 合并的时候,启发式合并,然后暴力修改 最多O(n)个集合,每个集合记录点权最大值 查询的时候 二分找到这个时间段 查询集合点权最大值即可 )

    可以做到:空间O(nlogn)时间O(nlogn)

    %%ImmortalCO

    可持久化平衡树:

    1.还是主席树做即可。

    权值暴力开到-1e9~1e9(我脑残了一下,还加了偏移量。。。)因为动态开点。。空间限制1GB

    然后做就好了。

    注意前驱后继的写法;

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define mid (((ll)l+r)>>1)
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=5e5+5;
    const int U=2e9+1;
    const int P=1e9+1;
    const int inf=2147483647;
    int n;
    struct node{
        int ls,rs,sz;
    }t[40*N];
    int rt[N];
    int tot;
    void pushup(int x){
        t[x].sz=t[t[x].ls].sz+t[t[x].rs].sz;
    }
    void ins(int &x,int y,int l,int r,int to){
        //
        x=++tot;
        t[x].ls=t[y].ls,t[x].rs=t[y].rs;
        t[x].sz=t[y].sz+1;
        if(l==r) return;
        if(to<=mid) ins(t[x].ls,t[y].ls,l,mid,to);
        else ins(t[x].rs,t[y].rs,mid+1,r,to);
    }
    void dele(int &x,int y,int l,int r,int to){
    //    cout<<" deleting "<<to<<endl;
        x=++tot;
        t[x].ls=t[y].ls,t[x].rs=t[y].rs;
        t[x].sz=t[y].sz;
        if(l==r){
            if(t[x].sz>=1) t[x].sz--;
            return;
        }
        if(to<=mid) dele(t[x].ls,t[y].ls,l,mid,to);
        else dele(t[x].rs,t[y].rs,mid+1,r,to);
        pushup(x);
    }
    int rk(int x,int l,int r,int c){
        if(l==r){
            return (l<c)*t[x].sz;
        }
        if(c<=mid) return rk(t[x].ls,l,mid,c);
        else return t[t[x].ls].sz+rk(t[x].rs,mid+1,r,c);
    }
    int kth(int x,int l,int r,int k){
        //cout<<l<<" "<<r<<" "<<mid<<" kkk "<<k<<" "<<t[x].sz<<endl;
        if(l==r)return l;
        int d=k-t[t[x].ls].sz;
        if(d<=0) return kth(t[x].ls,l,mid,k);
        else return kth(t[x].rs,mid+1,r,d);
    }
    int pre(int x,int l,int r,int c){
        if(l>=c||t[x].sz==0) return -inf;
        else if(l==r) return l;
        else{
            int ret=pre(t[x].rs,mid+1,r,c);
            if(ret!=-inf) return ret;
            return pre(t[x].ls,l,mid,c);
        }
    }
    int bac(int x,int l,int r,int c){
        //cout<<l<<" "<<r<<" "<<" : "<<t[x].sz<<endl;
        if(r<=c||t[x].sz==0) return inf;
        else if(l==r) return l;
        else{
            int ret=bac(t[x].ls,l,mid,c);
            if(ret!=inf) return ret;
            return bac(t[x].rs,mid+1,r,c);
        }
    }
    int main(){
        scanf("%d",&n);
        int st,op,x;
        int o=0;
        while(n--){
            rd(st),rd(op);rd(x);
            x+=P;
            ++o;
            rt[o]=rt[st];
            switch(op){
                case 1:ins(rt[o],rt[st],1,U,x);break;
                case 2:dele(rt[o],rt[st],1,U,x);break;
                case 3:printf("%d
    ",rk(rt[o],1,U,x)+1);break;
                case 4:{
                    int tmp=kth(rt[o],1,U,x-P);
                    //cout<<" tmp "<<tmp<<" "<<tmp-1e9<<" "<<tmp-1e9-1<<endl;
                    printf("%d
    ",tmp-P);
                    break;
                }
                case 5:{
                    int tmp=pre(rt[o],1,U,x);
                    if(tmp>=1&&tmp<=U){
                        printf("%d
    ",tmp-P);
                    }
                    else printf("%d
    ",tmp);//not find
                    break;
                }
                case 6:{
                    int tmp=bac(rt[o],1,U,x);
                    if(tmp>=1&&tmp<=U){
                        printf("%d
    ",tmp-P);
                    }
                    else printf("%d
    ",tmp);//not find
                    break;
                }
            }
            //cout<<" num "<<o<<" : "<<" tot "<<tot<<" sz "<<rt[o]<<" "<<t[rt[o]].sz<<endl;
        }
        return 0;
    }
    
    }
    int main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/11/23 9:19:03
    */
    可持久化平衡树

    2.fhq-Treap?

    留坑

    [学习笔记]FHQ-Treap及其可持久化

     例题:bzoj3946: 无聊的游戏

    可持久化0/1Trie

    其实就类似于主席树。(哪里都是主席树啊。。。)

    维护一个序列前缀的信息。

    每次加入一个点,在前一个的基础上,加入的是一个log(val)的链。

    额外维护一个sz,表示,前i个位置,走到这个位置,往下还有多少个数。(就类似于主席树)

    然后,给一个x,如果要找区间最大异或值,直接sz差分,判断有无,然后贪心走即可。

    例题:模板:最大异或和

    变一下形,就可以当“给一个x,找区间一个值异或,使得值最大”

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=300000+5;
    const int U=23;
    int n,m;
    struct trie{
        int ch[2];
        int sz;
    }t[48*N];
    int tot;
    int s;
    int rt[N+N];
    void ins(int id,int v){
        rt[id]=++tot;
        int x=rt[id],y=rt[id-1];
        for(reg i=U;i>=0;--i){
            int c=(v>>i)&1;
            t[x].ch[!c]=t[y].ch[!c];
            t[x].ch[c]=++tot;
            x=t[x].ch[c];
            y=t[y].ch[c];
            t[x].sz=t[y].sz+1;
        }
    }
    int query(int l,int r,int v){
        int y=l-1>=0?rt[l-1]:rt[0],x=rt[r];
        int ret=0;
        for(reg i=U;i>=0;--i){
            int c=(v>>i)&1;
            int d=t[t[x].ch[!c]].sz-t[t[y].ch[!c]].sz;
            if(d){
                ret+=(1<<i);
                x=t[x].ch[!c];y=t[y].ch[!c];
            }
            else{
                x=t[x].ch[c];y=t[y].ch[c];
            }
        }
        //cout<<l<<" "<<r<<" "<<x<<endl;
        if(l==0) ret=max(ret,v);
        return ret;
    }
    int main(){
        scanf("%d%d",&n,&m);
        int x;
        for(reg i=1;i<=n;++i){
            rd(x),s^=x,ins(i,s);
        }
        char ch[10];int l,r;
        int now=n;
        while(m--){
            scanf("%s",ch+1);
            //cout<<"ss "<<s<<endl;
            switch(ch[1]){
                case 'A':rd(x);s^=x;ins(++now,s);break;
                case 'Q':{
                    rd(l);rd(r);rd(x);
                    --l,--r;
                    x=s^x;
                    printf("%d
    ",query(l,r,x));
                    break;
                }
            }
            //cout<<" mmm "<<m<<endl;
        }
        return 0;
    }
    
    }
    int main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/11/23 14:22:23
    */
    最大异或和

    [TJOI2018]异或

    维护两个可持久化Trie,一个dfn序,处理子树。一个维护到树根的信息。

    子树,dfn序直接查询

    路径,拆成x到lca,y到lca分别差分查询。

    注意数组大小,根节点还有2*N个空间。。。。。

    31*N*2+2*N

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=100000+5;
    struct trie{
        int ch[2];
        int sz;
    }t[31*N*2+2*N];
    int tot;
    int df,dfn[N],fdfn[N],dfn2[N];
    int fa[N][20];
    int dep[N];
    int a[N];
    int n,m;
    struct node{
        int nxt,to;
    }e[2*N];
    int hd[N],cnt;
    void add(int x,int y){
        e[++cnt].nxt=hd[x];
        e[cnt].to=y;
        hd[x]=cnt;
    }
    int rt1[N];
    int lca(int x,int y){
        if(dep[x]<dep[y]) swap(x,y);
        for(reg j=19;j>=0;--j){
            if(dep[fa[x][j]]>=dep[y]) x=fa[x][j];
        }
        if(x==y) return x;
        for(reg j=19;j>=0;--j){
            if(fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
        }
        return fa[x][0];
    }
    void ins1(int x,int y,int v){
        rt1[x]=++tot;
        x=rt1[x];
        for(reg i=30;i>=0;--i){
            int c=(v>>i)&1;
            t[x].ch[!c]=t[y].ch[!c];
            t[x].ch[c]=++tot;
            x=t[x].ch[c];
            y=t[y].ch[c];
            t[x].sz=t[y].sz+1;
        }
    }
    void dfs(int x,int d){
        dep[x]=d;
        dfn[x]=++df;fdfn[df]=x;
        ins1(x,rt1[fa[x][0]],a[x]);
        for(reg i=hd[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(y==fa[x][0]) continue;
            fa[y][0]=x;
            dfs(y,d+1);
        }
        dfn2[x]=df;
    }
    int query1(int y,int x,int v){
        x=rt1[x];y=rt1[y];
        int ret=0;
        for(reg i=30;i>=0;--i){
            int c=(v>>i)&1;
            int d=t[t[x].ch[!c]].sz-t[t[y].ch[!c]].sz;
            if(d){
                ret+=(1<<i);
                x=t[x].ch[!c];y=t[y].ch[!c];
            }
            else {
                x=t[x].ch[c];y=t[y].ch[c];
            }
        }
        return ret;
    }
    int rt2[N];
    void ins2(int x,int v){
        int y=rt2[x-1];
        rt2[x]=++tot;
        x=rt2[x];
        for(reg i=30;i>=0;--i){
            int c=(v>>i)&1;
            t[x].ch[!c]=t[y].ch[!c];
            t[x].ch[c]=++tot;
            x=t[x].ch[c];
            y=t[y].ch[c];
            t[x].sz=t[y].sz+1;
        }
    }
    int query2(int y,int x,int v){
        x=rt2[x];y=rt2[y];
        int ret=0;
        for(reg i=30;i>=0;--i){
            int c=(v>>i)&1;
            int d=t[t[x].ch[!c]].sz-t[t[y].ch[!c]].sz;
            if(d){
                ret+=(1<<i);
                x=t[x].ch[!c];y=t[y].ch[!c];
            }
            else {
                x=t[x].ch[c];y=t[y].ch[c];
            }
        }
        return ret;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(reg i=1;i<=n;++i){
            rd(a[i]);
        }
        int x,y;
        for(reg i=1;i<=n-1;++i){
            rd(x);rd(y);add(x,y);add(y,x);
        }
        dep[0]=-1;
        dfs(1,1);
        for(reg j=1;j<=19;++j){
            for(reg i=1;i<=n;++i){
                fa[i][j]=fa[fa[i][j-1]][j-1];
            }
        }
        
        for(reg i=1;i<=n;++i){
            ins2(i,a[fdfn[i]]);
        }
        int op;
        int z;
        while(m--){
            scanf("%d",&op);
            if(op==1){
                rd(x);rd(y);
                printf("%d
    ",query2(dfn[x]-1,dfn2[x],y));
            }
            else{
                rd(x);rd(y);rd(z);
                int anc=lca(x,y);
                printf("%d
    ",max(query1(fa[anc][0],x,z),query1(fa[anc][0],y,z)));
            }
        }
        return 0;
    }
    
    }
    int main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/11/23 16:08:01
    */
    异或

    可持久化用途

    可持久化目的主要就是充分利用不会动的信息,减少时空的浪费

    1.历史值查询:模板,以及可持久化trie和主席树的差分

    2.路径压缩,任意字符集AC自动机

    3.当做标记:bzoj3946: 无聊的游戏

  • 相关阅读:
    Unity Behaviors for Interception
    Unity Method Dependency Injection
    Unity的AdditionalInterfaces问题
    Unity的三种Interceptor
    C#实现office文档转换为PDF或xps的一些方法
    多级菜单CSS+DIV
    应用软件系统架构设计的“七种武器”(转)
    AjaxModalPopupExtender
    越来越苦,献给狗日的小温
    NHibernate教程
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10006784.html
Copyright © 2011-2022 走看看