可持久化 trie 的简单入门
可持久化 $trie$ ....又是一个表里不一的东西.....
可持久化 $trie$ 的介绍:
和主席树类似的,其实可持久化就是体现在前缀信息的维护上(搞不懂这怎么就叫做可持久化了...)
$trie$ (字典树)大家应该都知道,就是一棵用来做字符串匹配的树,
但是!在这里,可持久化 $trie$ 就是完全不一样的东西了...
基本上(我做过的题),可持久化都是用来维护 $XOR$ 信息的...
比如说求某个范围内的最大区间异或和之类的,至于到了树上嘛,你懂的.
可持久化 $trie$ 的实现:
还是和主席树类似的,可持久化 $trie$ 就是要你在一棵树上(由于是异或,数字都会变成二进制,值只有 0 和 1 两种表示,于是这棵树自然就是二叉树了)维护每个前缀出现的次数(这里就是类似 trie 的做法)
哎...相信你是没有看懂的...于是边看代码边自己感性理解一下吧....
可持久化 $trie$ 的代码实现:
这其实是一道板子题的代码...
大体思路就是和主席树差不多,如果当前处理到了 0 ,那么 当前节点的 1 的孩子直接调用 las 所指向的孩子 1 就好了,
然后当前节点 和 las 节点都跳向 0 这个孩子,并且处理的这个过程是从高位到低位的(以符合查询时贪心的思想)
每次更新都是新增 30 (一般来说是这样,具体得看题目的数据范围) 个节点,所以不会炸
代码如下:
- //by Judge
- #include<iostream>
- #include<cstdio>
- using namespace std;
- const int M=3e7+;
- inline int read(){
- int x=,f=; char c=getchar();
- for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
- for(;isdigit(c);c=getchar()) x=x*+c-''; return x*f;
- }
- inline int cread(){
- char c=getchar(); while(c!='Q' && c!='A') c=getchar(); return c^'Q';
- }
- int n,m,cnt;
- int rt[M],son[M][],d[],sum[M];
- inline void split(int k){
- int i,len=;
- while(k) d[++len]=k&,k>>=;
- for(int i=len+;i<=;++i) d[i]=;
- }
- inline void update(int& now,int las){
- sum[now=++cnt]=sum[las]+;
- int i,tmp=now;
- for(i=;i;--i){
- son[tmp][d[i]^]=son[las][d[i]^],
- son[tmp][d[i]]=++cnt,las=son[las][d[i]],
- sum[tmp=cnt]=sum[las]+;
- }
- }
- inline int query(int u,int v){
- int ans=,i;
- for(i=;i;--i){
- if(sum[son[v][d[i]^]]-sum[son[u][d[i]^]]>)
- ans|=(<<i-),u=son[u][d[i]^],v=son[v][d[i]^];
- else u=son[u][d[i]],v=son[v][d[i]];
- } return ans;
- }
- int main(){
- int sum=,x,opt,l,r;
- n=read(),m=read(),++n;
- split(),update(rt[],rt[]);
- for(int i=;i<=n;++i)
- split(sum^=x=read()),
- update(rt[i],rt[i-]);
- for(int i=;i<=m;++i){
- opt=cread();
- if(opt)
- split(sum^=x=read()),
- update(rt[n+],rt[n]),++n;
- else
- l=read(),r=read(),x=read(),split(x^sum),
- printf("%d\n",query(rt[l-],rt[r]));
- } return ;
- }
view code
可持久化 $trie$ 的例题:
其实上面已经是一道了。
然后这道(树上搞事情)的题:Tree
其实树上 可持久化 trie 和树上主席树类似,就是当前节点调用的 las 节点变成了该节点的父节点,查询的时候也是和树上主席树类似的套路,
这里和树上主席树一样是要查询 LCA 的,我们用树剖维护即可(而且还可以在树剖时维护每个节点的可持久化信息)
代码如下:
- //by Judge
- #include<iostream>
- #include<cstring>
- #include<cstdio>
- using namespace std;
- const int M=1e5+;
- inline int read(){
- int x=,f=; char c=getchar();
- for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
- for(;isdigit(c);c=getchar()) x=x*+c-''; return x*f;
- }
- int n,m,pat,cnt;
- int head[M],d[],rt[M],to[M<<][],sum[M<<];
- int val[M],siz[M],dep[M],top[M],f[M],son[M];
- struct Edge{
- int to,next;
- Edge(int to,int next): to(to),next(next){} Edge(){}
- }e[M<<];
- inline void add(int u,int v){
- e[++pat]=Edge(v,head[u]),head[u]=pat;
- e[++pat]=Edge(u,head[v]),head[v]=pat;
- }
- /************* 模板 ********************/
- inline void split(int k){
- int len=,i;
- while(k) d[++len]=k&,k>>=;
- for(i=len+;i<=;++i) d[i]=;
- }
- inline void update(int& root,int las){
- int now=root=++cnt;
- sum[now]=sum[las]+;
- for(int i=;i;--i){
- to[now][d[i]^]=to[las][d[i]^],
- to[now][d[i]]=++cnt,las=to[las][d[i]],
- now=cnt,sum[now]=sum[las]+;
- }
- }
- #define v e[i].to
- void dfs1(int u,int fa){
- siz[u]=,son[u]=top[u]=;
- split(val[u]),update(rt[u],rt[fa]);
- for(int i=head[u];i;i=e[i].next) if(v!=fa){
- f[v]=u,dep[v]=dep[u]+,dfs1(v,u),siz[u]+=siz[v];
- if(siz[v]>siz[son[u]]) son[u]=v;
- }
- }
- void dfs2(int u){
- if(!top[u]) top[u]=u; if(!son[u]) return ;
- top[son[u]]=top[u],dfs2(son[u]);
- for(int i=head[u];i;i=e[i].next)
- if(v!=son[u] && v!=f[u]) dfs2(v);
- }
- #undef v
- inline int LCA(int u,int v){
- while(top[u]^top[v])
- dep[top[u]]>dep[top[v]]?u=f[top[u]]:v=f[top[v]];
- return dep[u]<dep[v]?u:v;
- }
- /* 程序 */
- inline int query(int u,int v,int lca,int f_lca){
- int ans=;
- for(int i=;i;--i){
- if(sum[to[u][d[i]^]]+sum[to[v][d[i]^]]-sum[to[lca][d[i]^]]-sum[to[f_lca][d[i]^]])
- ans|=(<<i-),u=to[u][d[i]^],v=to[v][d[i]^],lca=to[lca][d[i]^],f_lca=to[f_lca][d[i]^];
- else u=to[u][d[i]],v=to[v][d[i]],lca=to[lca][d[i]],f_lca=to[f_lca][d[i]];
- } return ans;
- }
- int x,y,z,lca;
- inline void query(){
- x=read(),y=read(),z=read(),lca=LCA(x,y),split(z);
- printf("%d\n",query(rt[x],rt[y],rt[lca],rt[f[lca]]));
- }
- int main(){
- while(~scanf("%d%d",&n,&m)){
- pat=cnt=,memset(head,,sizeof(head));
- for(int i=;i<=n;++i) val[i]=read();
- for(int i=,u,v;i<n;++i)
- u=read(),v=read(),add(u,v);
- dfs1(,),dfs2(); while(m--) query();
- } return ;
- }
然后就是这题(TM做了我一晚上就在那里 TLE、 MLE 、WA 各种挂): L
这道题...够恶心的,又是区间内询问区间...
而且更恶心的是,你要用分块的算法去优化算法...难以想到(其实打起来也还好)
$a[i]$ 代表 1 ~ i 的 前缀异或和
$f[i][j]$ 代表以第 i * block 这个位置开始,到 j-1 结束的区间内的前缀异或和中,与 a[j] 异或的最大值
代码如下:
- //by Judge
- #include<cmath>
- #include<cstdio>
- #include<iostream>
- #define ll long long
- using namespace std;
- const int M=;
- char buf[<<],*p1,*p2;
- #define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
- inline int read(){
- int x=,f=; char c=getchar();
- for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
- for(;isdigit(c);c=getchar()) x=x*+c-''; return x*f;
- }
- char sr[<<],z[];int C=-,Z;
- inline void Ot(){fwrite(sr,,C+,stdout),C=-;}
- inline void print(ll x){
- if(C><<)Ot();if(x<)sr[++C]=,x=-x;
- while(z[++Z]=x%+,x/=);
- while(sr[++C]=z[Z],--Z);sr[++C]='\n';
- }
- int n,m,block,cnt,a[M<<],f[][M];
- int d[],rt[M<<],to[M<<][],sum[M<<];
- inline void split(int k){
- int len=; while(k) d[++len]=k&,k>>=;
- for(int i=len+;i<=;++i) d[i]=;
- }
- inline void update(int& root,int las){
- int now=root=++cnt; sum[now]=sum[las]+;
- for(int i=;i;--i){
- to[now][d[i]^]=to[las][d[i]^];
- to[now][d[i]]=++cnt,las=to[las][d[i]];
- sum[now=cnt]=sum[las]+;
- }
- }
- inline ll query(int u,int v){
- ll ans=;
- for(int i=;i;--i){
- if(sum[to[v][d[i]^]]-sum[to[u][d[i]^]])
- ans|=1ll<<i-,u=to[u][d[i]^],v=to[v][d[i]^];
- else u=to[u][d[i]],v=to[v][d[i]];
- } return ans;
- }
- int main(){
- n=read(),m=read(),update(rt[],); int x,y,l,r,s,i,j; ll ans=;
- for(i=;i<=n;++i) a[i]=read()^a[i-],split(a[i]),update(rt[i],rt[i-]);
- for(block=(int)sqrt(n+)+,i=;i<=n;i+=block) for(j=i+;j<=n;++j)
- split(a[j]),f[i/block][j]=max(1ll*f[i/block][j-],query(i?rt[i-]:,rt[j-]));
- while(m--){
- x=read(),y=read(),
- r=max((1ll*x+ans)%n+,(1ll*y+ans)%n+),
- s=l=min((1ll*x+ans)%n+,(1ll*y+ans)%n+)-;
- while(s%block && s<r) ++s;
- if(s==r){
- for(ans=,j=l+;j<=r;++j)
- split(a[j]),ans=max(ans,query(l?rt[l-]:,rt[j-]));
- } else{
- for(ans=f[s/block][r],j=s-;j>=l;--j)
- split(a[j]),ans=max(ans,query(rt[j],rt[r]));
- } print(ans);
- } Ot(); return ;
- }
其实这是一道省选题(已填坑): 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+;
- const int inf=1e9+;
- char buf[<<],*p1,*p2;
- #define getchar() (p1==p2 && (p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
- inline int read(){
- int x=,f=; char c=getchar();
- for(;!isdigit(c);c=getchar()) if(c=='-') f=-;
- for(;isdigit(c);c=getchar()) x=x*+c-''; return x*f;
- }
- int n,cnt,ans; set<int> q;
- int d[],rt[M],sum[M<<],son[M<<][];
- 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=; while(k) d[++len]=k&,k>>=;
- for(int i=len+;i<=;++i) d[i]=;
- }
- inline void update(int& nw,int las){
- int now=nw=++cnt; sum[now]=sum[las]+;
- for(int i=;i;--i){
- son[now][d[i]^]=son[las][d[i]^];
- son[now][d[i]]=++cnt,las=son[las][d[i]];
- sum[now=cnt]=sum[las]+;
- }
- }
- inline int query(int u,int v){
- int ans=; for(int i=;i;--i){
- if(sum[son[v][d[i]^]]-sum[son[u][d[i]^]])
- ans|=(<<i-),u=son[u][d[i]^],v=son[v][d[i]^];
- else u=son[u][d[i]],v=son[v][d[i]];
- } return ans;
- }
- int main(){
- n=read(); for(int i=;i<=n;++i) a[i].val=read(),a[i].id=i;
- for(int i=;i<=n;++i) split(a[i].val),update(rt[i],rt[i-]);
- q.insert(-),q.insert(inf),q.insert(-),q.insert(inf+),
- sort(a+,a++n),q.insert(a[].id);
- for(int i=;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-,--p,--p,l=*p+,l=max(,l),r=min(r,n),q.insert(x);
- if(l^r) split(a[i].val),ans=max(ans,query(rt[l-],rt[r]));
- } printf("%d\n",ans); return ;
- }
这里用的就是set ,不过你手打 splay 也是没问题的
emmmmm...可持久化 trie 的题还是蛮少的...
可持久化 trie 的简单入门的更多相关文章
- 可持久化trie 学习总结
QAQ 以前一直觉得可持久化trie很难,今天强行写了一发觉得还是蛮简单的嘛 自己的模板是自己手写的,写了几道题目并没有出过错误 THUSC的第二题的解法五貌似就是可持久化trie,时间复杂度O(60 ...
- 可持久化trie学习笔记
其实很早之前就想学习可持久化trie,不过由于换队友等情况,还是优先去学数论和计算几何,今天突然心血来潮学了一发可持久化trie,感觉还是蛮简单的,不过由于自己很长时间没写过可持久化了,都快忘了是个什 ...
- Vue的简单入门
Vue的简单入门 一.什么是Vue? vue.js也一个渐进式JavaScript框架,可以独立完成前后端分离式web项目 渐进式:vue可以从小到控制页面中的一个变量后到页面中一块内容再到整个页面, ...
- 可持久化Trie
---恢复内容开始--- HAOI 2019 DAY1 T1 我爆零了. 爆零的感觉很难受 原因竟然是我从没犯过的错误 审题不清.情绪低迷. 也许 也许 也许就是想让我知道我有多菜吧. 求前k大的区间 ...
- HDU.4757.Tree(可持久化Trie)
题目链接 \(Description\) 给定一棵树,点有点权.\(Q\)次询问\(x,y,z\),求\(x\)到\(y\)的简单路径中,与\(z\)异或能得到的最大的数是多少. \(Solution ...
- bzoj3261: 最大异或和 可持久化trie
题意:给定一个非负整数序列{a},初始长度为N. 有M个操作,有以下两种操作类型: 1.Ax:添加操作,表示在序列末尾添加一个数x,序列的长度N+1. 2.Qlrx:询问操作,你需要找到一个位置p,满 ...
- [转]Scrapy简单入门及实例讲解
Scrapy简单入门及实例讲解 中文文档: http://scrapy-chs.readthedocs.io/zh_CN/0.24/ Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用 ...
- 【xsy1214】 异或路径(xorpath) 点分治+可持久化trie
题目大意:给你一棵$n$个点的树,每个点有一个点权$x$,问你所有路径中点权异或和最大的路径的异或和 数据范围:$n≤30000$,$x≤2^{31}-1$. 如果是边上有点权的话非常简单,直接一个$ ...
- CQRS简单入门(Golang)
一.简单入门之入门 CQRS/ES和领域驱动设计更搭,故整体分层沿用经典的DDD四层.其实要实现的功能概要很简单,如下图. 基础框架选择了https://github.com/looplab/even ...
随机推荐
- 洛谷 P5020 货币系统
题目描述 在网友的国度中共有$ n $种不同面额的货币,第 i种货币的面额为 \(a[i]\),你可以假设每一种货币都有无穷多张.为了方便,我们把货币种数为\(n\).面额数组为 \(a[1..n]\ ...
- 越狱解决iphone4s外放无声音
删除iphone中/System/Library/PrivateFrameworks/IAP.framework/Support/目录下的iapd文件 进入/SYSTEM/Library/Launch ...
- [Spark][Streaming]Spark读取网络输入的例子
Spark读取网络输入的例子: 参考如下的URL进行试验 https://stackoverflow.com/questions/46739081/how-to-get-record-in-strin ...
- Vue slot插槽内容分发
slot插槽使用 使用场景,一般父组件中又一大段模板内容需要运用到子组件上.或者更加复杂的,子组件需要运用到父组件大段模板内容,而子组件却不知道挂载的内容是什么.挂载点的内容是由父组件来决定的. Sl ...
- docker 小技巧 docker network create br-name 指定IP地址
在某些情况下,使用 docker network create br-name 命令创建网络的时候,会创建一个新的网桥,该网桥的默认IP地址为172.18.0.0\16(或相临的IP地址段) 这个ip ...
- react的jsx语法
在webpack.config.js中配置解析的loader { test:/\.jsx?$/, use:{ loader:"babel-loader", options:{ pr ...
- python之面向对象初识
一.面向对象初识 1.结构上 面向对象分成两部分:属性.方法 class A: name = 'xiaoming' # 静态属性.静态变量.静态字段. def func1(self): # 函数.动态 ...
- x86汇编语言实践(1)
0 写在前面 为了更深入的了解程序的实现原理,近期我学习了IBM-PC相关原理,并手工编写了一些x86汇编程序. 在2017年的计算机组成原理中,曾对MIPS体系结构及其汇编语言有过一定的了解,考虑到 ...
- CF5E Bindian Signalizing
题目 这题目是真的很水,洛谷给他紫题也差不多算恶意评分了吧233 这种一眼切的题改了很长时间,不是什么n-1搞错,就是什么and打成or,所以写这篇博客给自己长个记性QWQ 题意:n座山组成一个环,相 ...
- 洛谷P3369 普通平衡树
刚学平衡树,分别用了Splay和fhq-treap交了一遍. 这是Splay的板子,貌似比较短? Splay #include <iostream> #include <cstdio ...