可持久化 trie 的简单入门

摘要:
可持久化$trie$....又是一个表里不一的东西.....可持久化$trie$的介绍:和主席树类似的,其实可持久化就是体现在前缀信息的维护上$trie$(字典树)大家应该都知道,就是一棵用来做字符串匹配的树,但是!然后这道的题:Tree其实树上可持久化trie和树上主席树类似,就是当前节点调用的las节点变成了该节点的父节点,查询的时候也是和树上主席树类似的套路,这里和树上主席树一样是要查询LCA的,我们用树剖维护即可代码如下:1//byJudge2#include3#include4#include5usingnamespacestd;6constintM=1e5+111;7inlineintread(){8intx=0,f=1;charc=getchar();9for(;!

可持久化 $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 namespacestd;
5 const int M=3e7+111;
6 inline intread(){
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 intcread(){
12     char c=getchar(); while(c!='Q' && c!='A') c=getchar(); return c^'Q';
13 }
14 intn,m,cnt;
15 int rt[M],son[M][2],d[30],sum[M];
16 inline void split(intk){
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,intlas){
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,intv){
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     } returnans;
37 }
38 intmain(){
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 namespacestd;
6 const int M=1e5+111;
7 inline intread(){
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 intn,m,pat,cnt;
13 int head[M],d[20],rt[M],to[M<<5][2],sum[M<<5];
14 intval[M],siz[M],dep[M],top[M],f[M],son[M];
15 structEdge{
16     intto,next;
17     Edge(int to,intnext): to(to),next(next){} Edge(){}
18 }e[M<<1];
19 inline void add(int u,intv){
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(intk){
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,intlas){
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,intfa){
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(intu){
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,intv){
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,intf_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     } returnans;
67 }
68 intx,y,z,lca;
69 inline voidquery(){
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 intmain(){
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 namespacestd;
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 intread(){
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 voidprint(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(intk){
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,intlas){
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,intv){
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     } returnans;  
43 }
44 intmain(){
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 namespacestd;
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 intread(){
    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{ intid,val; }a[M];
inline bool operator <(Node& a,Node&b){
    return a.val>b.val;
}
inline void split(intk){
    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,intlas){
    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,intv){
    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]];
    } returnans;
}
intmain(){
    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 的题还是蛮少的...

免责声明:文章转载自《可持久化 trie 的简单入门》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇[Unity3D]引擎崩溃、异常、警告、BUG与提示总结及解决方法Unity3d + NGUI 的多分辨率适配(黑边)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

rabbitmq进阶

目录 消息传递 过期时间(TTL) 死信队列 延迟队列 优先级队列 RPC实现 持久化 生产者确认 消费端要点 消息传输保障 消息传递 mandatory mandatory=true,如果交换器无法根据自身的类型和路由键找到一个符合条件的队列,RabbitMQ会调用 Basic.Return 命令将消息返回给生产者,生产者通过调用 chann...

使用docker容器运行MySQL数据库并持久化数据文件

1、下载mysql镜像 # docker pull mysql 2、启动mysql容器 # docker run -itd -v /data:/var/lib/mysql -p 33060:3306 --name mysqldb mysql bash WARNING: IPv4 forwarding is disabled. Networking will...

[2020牛客暑期多校训练营(第一场)虚树 Infinite Tree]

2020牛客暑期多校训练营(第一场)虚树 Infinite Tree 题解参考博客:https://blog.nowcoder.net/n/df889adfaf824d50ad2291f4d2eb04a2?&toCommentId=6480068 题目大意: 定义 (mindiv(n)) 是 (n) 最小的大于1的约数,对于每一个 (i),(1&l...

shell学习(18)- split切分文件命令

Linux split命令用于将一个文件分割成数个。 该指令将大文件分割成较小的文件,在默认情况下将按照每1000行切割成一个小文件。 语法: split [--help][--version][-<行数>][-b <字节>][-C <字节>][-l <行数>][要切割的文件][输出文件名] 参数: -&l...

Python 持久化管理之 Pickle/ZODB

1.对象持久化 如果希望透明地存储 Python 对象,而不丢失其身份和类型等信息,则需要某种形式的对象序列化: 它是一个将任意复杂的对象转成对象的文本或二进制表示的过程。同样,必须能够将对象经过序列化后的形式恢复到原有的对象。 在 Python 中,这种序列化过程称为 pickle,可以将对象 pickle 成字符串、磁盘上的文件或者任何类似于文件的对象...

【Java】分割字符串并实现去重(重复的分割字符)

原始字符串:" Mem: 4194304 4134400 59904 0 0 2572228"需求:把原始字符串中的有效字符提取出来(有效字符指:非空白字符) 即预期为“Mem:”,“4194304”,“4134400”,“59904”,“0”,“0”,“2572228”这...