• 可持久化 trie 的简单入门


    可持久化 $trie$  ....又是一个表里不一的东西.....

    可持久化 $trie$  的介绍:

    和主席树类似的,其实可持久化就是体现在前缀信息的维护上(搞不懂这怎么就叫做可持久化了...)

     $trie$ (字典树)大家应该都知道,就是一棵用来做字符串匹配的树,

    但是!在这里,可持久化 $trie$ 就是完全不一样的东西了...

    基本上(我做过的题),可持久化都是用来维护  $XOR$   信息的...

    比如说求某个范围内的最大区间异或和之类的,至于到了树上嘛,你懂的.


    可持久化 $trie$  的实现:

    还是和主席树类似的,可持久化 $trie$   就是要你在一棵树上(由于是异或,数字都会变成二进制,值只有 0 和 1 两种表示,于是这棵树自然就是二叉树了)维护每个前缀出现的次数(这里就是类似 trie 的做法)

    哎...相信你是没有看懂的...于是边看代码边自己感性理解一下吧.... 


    可持久化 $trie$ 的代码实现:

    这其实是一道板子题的代码...

    大体思路就是和主席树差不多,如果当前处理到了 0 ,那么 当前节点的  1  的孩子直接调用  las  所指向的孩子 1  就好了,

    然后当前节点 和 las 节点都跳向 0 这个孩子,并且处理的这个过程是从高位到低位的(以符合查询时贪心的思想)

    每次更新都是新增 30 (一般来说是这样,具体得看题目的数据范围) 个节点,所以不会炸

    代码如下:

     1 //by Judge
     2 #include<iostream>
     3 #include<cstdio>
     4 using namespace std;
     5 const int M=3e7+111;
     6 inline int read(){
     7     int x=0,f=1; char c=getchar();
     8     for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
     9     for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
    10 }
    11 inline int cread(){
    12     char c=getchar(); while(c!='Q' && c!='A') c=getchar(); return c^'Q';
    13 }
    14 int n,m,cnt;
    15 int rt[M],son[M][2],d[30],sum[M];
    16 inline void split(int k){
    17     int i,len=0;
    18     while(k) d[++len]=k&1,k>>=1;
    19     for(int i=len+1;i<=27;++i) d[i]=0;
    20 }
    21 inline void update(int& now,int las){
    22     sum[now=++cnt]=sum[las]+1;
    23     int i,tmp=now;
    24     for(i=27;i;--i){
    25         son[tmp][d[i]^1]=son[las][d[i]^1],
    26         son[tmp][d[i]]=++cnt,las=son[las][d[i]],
    27         sum[tmp=cnt]=sum[las]+1;
    28     }
    29 }
    30 inline int query(int u,int v){
    31     int ans=0,i;
    32     for(i=27;i;--i){
    33         if(sum[son[v][d[i]^1]]-sum[son[u][d[i]^1]]>0)
    34             ans|=(1<<i-1),u=son[u][d[i]^1],v=son[v][d[i]^1];
    35         else u=son[u][d[i]],v=son[v][d[i]];
    36     } return ans;
    37 }
    38 int main(){
    39     int sum=0,x,opt,l,r;
    40     n=read(),m=read(),++n;
    41     split(0),update(rt[1],rt[0]);
    42     for(int i=2;i<=n;++i)
    43         split(sum^=x=read()),
    44         update(rt[i],rt[i-1]);
    45     for(int i=1;i<=m;++i){
    46         opt=cread();
    47         if(opt)
    48             split(sum^=x=read()),
    49             update(rt[n+1],rt[n]),++n;
    50         else
    51             l=read(),r=read(),x=read(),split(x^sum),
    52             printf("%d
    ",query(rt[l-1],rt[r]));
    53     } return 0;
    54 }
    view code

    可持久化 $trie$  的例题: 

     其实上面已经是一道了。

    然后这道(树上搞事情)的题:Tree

    其实树上 可持久化 trie  和树上主席树类似,就是当前节点调用的 las 节点变成了该节点的父节点,查询的时候也是和树上主席树类似的套路,

    这里和树上主席树一样是要查询  LCA  的,我们用树剖维护即可(而且还可以在树剖时维护每个节点的可持久化信息)

    代码如下:

     1 //by Judge
     2 #include<iostream>
     3 #include<cstring>
     4 #include<cstdio>
     5 using namespace std;
     6 const int M=1e5+111;
     7 inline int read(){
     8     int x=0,f=1; char c=getchar();
     9     for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    10     for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
    11 }
    12 int n,m,pat,cnt;
    13 int head[M],d[20],rt[M],to[M<<5][2],sum[M<<5];
    14 int val[M],siz[M],dep[M],top[M],f[M],son[M];
    15 struct Edge{
    16     int to,next;
    17     Edge(int to,int next): to(to),next(next){} Edge(){}
    18 }e[M<<1];
    19 inline void add(int u,int v){
    20     e[++pat]=Edge(v,head[u]),head[u]=pat;
    21     e[++pat]=Edge(u,head[v]),head[v]=pat; 
    22 }
    23 /*************         模板         ********************/
    24 inline void split(int k){
    25     int len=0,i;
    26     while(k) d[++len]=k&1,k>>=1;
    27     for(i=len+1;i<=18;++i) d[i]=0;
    28 }
    29 inline void update(int& root,int las){
    30     int now=root=++cnt;
    31     sum[now]=sum[las]+1;
    32     for(int i=18;i;--i){
    33         to[now][d[i]^1]=to[las][d[i]^1],
    34         to[now][d[i]]=++cnt,las=to[las][d[i]],
    35         now=cnt,sum[now]=sum[las]+1;
    36     }
    37 }
    38 #define v e[i].to
    39 void dfs1(int u,int fa){
    40     siz[u]=1,son[u]=top[u]=0;
    41     split(val[u]),update(rt[u],rt[fa]);
    42     for(int i=head[u];i;i=e[i].next) if(v!=fa){
    43         f[v]=u,dep[v]=dep[u]+1,dfs1(v,u),siz[u]+=siz[v];
    44         if(siz[v]>siz[son[u]]) son[u]=v;
    45     }
    46 }
    47 void dfs2(int u){
    48     if(!top[u]) top[u]=u; if(!son[u]) return ;
    49     top[son[u]]=top[u],dfs2(son[u]);
    50     for(int i=head[u];i;i=e[i].next)
    51         if(v!=son[u] && v!=f[u]) dfs2(v);
    52 }
    53 #undef v
    54 inline int LCA(int u,int v){
    55     while(top[u]^top[v])
    56         dep[top[u]]>dep[top[v]]?u=f[top[u]]:v=f[top[v]]; 
    57     return dep[u]<dep[v]?u:v;
    58 }
    59 /*          程序         */
    60 inline int query(int u,int v,int lca,int f_lca){
    61     int ans=0;
    62     for(int i=18;i;--i){
    63         if(sum[to[u][d[i]^1]]+sum[to[v][d[i]^1]]-sum[to[lca][d[i]^1]]-sum[to[f_lca][d[i]^1]])
    64             ans|=(1<<i-1),u=to[u][d[i]^1],v=to[v][d[i]^1],lca=to[lca][d[i]^1],f_lca=to[f_lca][d[i]^1];
    65         else u=to[u][d[i]],v=to[v][d[i]],lca=to[lca][d[i]],f_lca=to[f_lca][d[i]];
    66     } return ans;
    67 }
    68 int x,y,z,lca;
    69 inline void query(){
    70     x=read(),y=read(),z=read(),lca=LCA(x,y),split(z);
    71     printf("%d
    ",query(rt[x],rt[y],rt[lca],rt[f[lca]]));
    72 }
    73 int main(){
    74     while(~scanf("%d%d",&n,&m)){
    75         pat=cnt=0,memset(head,0,sizeof(head));
    76         for(int i=1;i<=n;++i) val[i]=read();
    77         for(int i=1,u,v;i<n;++i)
    78             u=read(),v=read(),add(u,v);
    79         dfs1(1,0),dfs2(1); while(m--) query();
    80     } return 0;
    81 }
    View Code

    然后就是这题(TM做了我一晚上就在那里 TLE、 MLE 、WA  各种挂): L

    这道题...够恶心的,又是区间内询问区间...

    而且更恶心的是,你要用分块的算法去优化算法...难以想到(其实打起来也还好)

    $a[i]$   代表 1 ~ i 的 前缀异或和

    $f[i][j]$  代表以第 i * block 这个位置开始,到 j-1 结束的区间内的前缀异或和中,与 a[j]  异或的最大值

    代码如下:

     1 //by Judge
     2 #include<cmath>
     3 #include<cstdio>
     4 #include<iostream>
     5 #define ll long long
     6 using namespace std;
     7 const int M=12111;
     8 char buf[1<<20],*p1,*p2;
     9 #define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
    10 inline int read(){
    11     int x=0,f=1; char c=getchar();
    12     for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    13     for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
    14 }
    15 char sr[1<<21],z[20];int C=-1,Z;
    16 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
    17 inline void print(ll x){
    18     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
    19     while(z[++Z]=x%10+48,x/=10);
    20     while(sr[++C]=z[Z],--Z);sr[++C]='
    ';
    21 }
    22 int n,m,block,cnt,a[M<<1],f[311][M];
    23 int d[50],rt[M<<1],to[M<<6][2],sum[M<<6];
    24 inline void split(int k){
    25     int len=0; while(k) d[++len]=k&1,k>>=1;
    26     for(int i=len+1;i<=32;++i) d[i]=0;
    27 }
    28 inline void update(int& root,int las){
    29     int now=root=++cnt; sum[now]=sum[las]+1;
    30     for(int i=32;i;--i){
    31         to[now][d[i]^1]=to[las][d[i]^1];
    32         to[now][d[i]]=++cnt,las=to[las][d[i]];
    33         sum[now=cnt]=sum[las]+1;
    34     }
    35 }
    36 inline ll query(int u,int v){
    37     ll ans=0;
    38     for(int i=32;i;--i){
    39         if(sum[to[v][d[i]^1]]-sum[to[u][d[i]^1]])
    40             ans|=1ll<<i-1,u=to[u][d[i]^1],v=to[v][d[i]^1];
    41         else u=to[u][d[i]],v=to[v][d[i]];
    42     } return ans;  
    43 }
    44 int main(){
    45     n=read(),m=read(),update(rt[0],0); int x,y,l,r,s,i,j; ll ans=0;
    46     for(i=1;i<=n;++i) a[i]=read()^a[i-1],split(a[i]),update(rt[i],rt[i-1]);
    47     for(block=(int)sqrt(n+1)+1,i=0;i<=n;i+=block) for(j=i+1;j<=n;++j)
    48         split(a[j]),f[i/block][j]=max(1ll*f[i/block][j-1],query(i?rt[i-1]:0,rt[j-1]));
    49     while(m--){
    50         x=read(),y=read(),
    51         r=max((1ll*x+ans)%n+1,(1ll*y+ans)%n+1),
    52         s=l=min((1ll*x+ans)%n+1,(1ll*y+ans)%n+1)-1;
    53         while(s%block && s<r) ++s;
    54         if(s==r){
    55             for(ans=0,j=l+1;j<=r;++j)
    56                 split(a[j]),ans=max(ans,query(l?rt[l-1]:0,rt[j-1]));
    57         } else{
    58             for(ans=f[s/block][r],j=s-1;j>=l;--j)
    59                 split(a[j]),ans=max(ans,query(rt[j],rt[r]));
    60         } print(ans);
    61     } Ot(); return 0;
    62 }
    View Code

    其实这是一道省选题(已填坑): Alo

    题目说的就是要找出一个区间,让该区间内的次大值异或上区间内的任意一个数,使得异或和最大

    坑... set 来维护已出现的下标,但是在使用 set 前居然要加入 -1、-2、inf、inf+1 四个元素...

    以防止访问越界的情况(我们是依次枚举那个次大值,然后要找到前、后比他大的第二近的元素下标,也就是说容易越界)

    然后这里还是要用到可(e)爱(xin)的前缀异或和

    代码如下:

    //by Judge
    #include<algorithm>
    #include<iostream>
    #include<cstdio>
    #include<set>
    using namespace std;
    const int M=1e5+111;
    const int inf=1e9+7;
    char buf[1<<20],*p1,*p2;
    #define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
    inline int read(){
        int x=0,f=1; char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
    }
    int n,cnt,ans;  set<int> q;
    int d[40],rt[M],sum[M<<5],son[M<<5][2];
    struct Node{ int id,val; }a[M];
    inline bool operator <(Node& a,Node& b){
        return a.val>b.val;
    }
    inline void split(int k){
        int len=0; while(k) d[++len]=k&1,k>>=1;
        for(int i=len+1;i<=30;++i) d[i]=0;
    }
    inline void update(int& nw,int las){
        int now=nw=++cnt; sum[now]=sum[las]+1;
        for(int i=30;i;--i){
            son[now][d[i]^1]=son[las][d[i]^1];
            son[now][d[i]]=++cnt,las=son[las][d[i]];
            sum[now=cnt]=sum[las]+1; 
        }
    }
    inline int query(int u,int v){
        int ans=0; for(int i=30;i;--i){
            if(sum[son[v][d[i]^1]]-sum[son[u][d[i]^1]])
                ans|=(1<<i-1),u=son[u][d[i]^1],v=son[v][d[i]^1];
            else u=son[u][d[i]],v=son[v][d[i]];
        } return ans;
    }
    int main(){
        n=read(); for(int i=1;i<=n;++i) a[i].val=read(),a[i].id=i;
        for(int i=1;i<=n;++i) split(a[i].val),update(rt[i],rt[i-1]);
        q.insert(-1),q.insert(inf),q.insert(-2),q.insert(inf+1),
        sort(a+1,a+1+n),q.insert(a[1].id);
        for(int i=2;i<=n;++i){
            int l=a[i].id,r=a[i].id,x=a[i].id;
            set<int>::iterator t,p; t=p=q.lower_bound(x);
            ++t,r=*t-1,--p,--p,l=*p+1,l=max(1,l),r=min(r,n),q.insert(x);
            if(l^r) split(a[i].val),ans=max(ans,query(rt[l-1],rt[r]));
        } printf("%d
    ",ans); return 0;
    }
    View Code

     这里用的就是set ,不过你手打 splay 也是没问题的

    emmmmm...可持久化 trie 的题还是蛮少的...

  • 相关阅读:
    Laravel5.0学习--03 Artisan命令
    yar框架使用笔记
    MySQL外键之级联
    笛卡尔积
    PHP钩子机制
    CentOS安装PHP7+Nginx+MySQL
    Linux压缩命令
    Git命令汇总
    使用List把一个长字符串分解成若干个短字符串
    Adb connection Error:远程主机强迫关闭了一个现有的连接
  • 原文地址:https://www.cnblogs.com/Judge/p/9498797.html
Copyright © 2020-2023  润新知