[学习笔记]可持久化数据结构——数组、并查集、平衡树、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\n",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\n",tmp-P);break;}case 5:{int tmp=pre(rt[o],1,U,x);if(tmp>=1&&tmp<=U){printf("%d\n",tmp-P);}else printf("%d\n",tmp);//not findbreak;}case 6:{int tmp=bac(rt[o],1,U,x);if(tmp>=1&&tmp<=U){printf("%d\n",tmp-P);}else printf("%d\n",tmp);//not findbreak;}}//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\n",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\n",query2(dfn[x]-1,dfn2[x],y));}else{rd(x);rd(y);rd(z);int anc=lca(x,y);printf("%d\n",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: 无聊的游戏
转载于:https://www.cnblogs.com/Miracevin/p/10006784.html
[学习笔记]可持久化数据结构——数组、并查集、平衡树、Trie树相关推荐
- [学习笔记]我们追过的神奇异或(Trie树系列)
引言 刚学了\(Trie\)树,写篇博客巩固一下. 题目 首先安利一发\(Trie\)树模板 1.Phone List 2.The XOR largest pair 3.The xor-longest ...
- Mysql学习笔记(六)增删改查
原文:Mysql学习笔记(六)增删改查 PS:数据库最基本的操作就是增删改查了... 学习内容: 数据库的增删改查 1.增...其实就是向数据库中插入数据.. 插入语句 insert into tab ...
- mongo学习笔记四:Mongodb的三种集群(Replica Set)
Replica Set 中文翻译叫做副本集,不过我并不喜欢把英文翻译成中文,总是感觉怪怪的.其实简单来说就是集群当中包含了多份数据,保证主节点挂掉了,备节点能继续提供数据服务,提供的前提就是数据 ...
- 大数据学习笔记第1课 Hadoop基础理论与集群搭建
大数据学习笔记第1课 Hadoop基础理论与集群搭建 一.环境准备 二.下载JDK 三.安装JDK 四.下载hadoop 五.安装hadoop集群 六.打通3台服务器的免密登录 七.hadoop集群配 ...
- 数据结构-PHP 并查集(Union Find)
文章目录 数据结构-PHP 并查集(Union Find) 1.并查集示意图 2.并查集合并 3.并查集简单的代码示例 3.1 PHP代码定义 3.2 输出演示 数据结构-PHP 并查集(Union ...
- 数据结构与算法学习笔记(python)——第一节 数组应用程序实战
前言 本人是一个长期的数据分析爱好者,最近半年的时间的在网上学习了很多关于python.数据分析.数据挖掘以及项目管理相关的课程和知识,但是在学习的过程中,过于追求课程数量的增长,长时间关注于学习了多 ...
- 算法学习笔记:简单数据结构及排序算法
原学习视频跳转地址:https://www.bilibili.com/video/BV13g41157hK?p=2 本文为自学视频整理的简单笔记 目录 排序 冒泡排序 选择排序 插入排序 归并排序(递 ...
- JavaScript学习笔记(三)——数组
在学习廖雪峰前辈的JavaScript教程中,遇到了一些需要注意的点,因此作为学习笔记列出来,提醒自己注意! 如果大家有需要,欢迎访问前辈的博客https://www.liaoxuefeng.com/ ...
- JavaScript学习笔记(六)--数组
数组初始化 我们都知道,数组是用于保存多个值的集合,在数组中,值被称为元素,值可以是任意的数据类型.在Javascript中,创建数组通常有两种方式:字面量和构造函数. 字面量 数组的元素可以是任意的 ...
最新文章
- 标称变量(Categorical Features)或者分类变量(Categorical Features​​​​​​​)编码为数值变量(Continuous Features​​​​​​​)
- util类中非静态方法中注入serivce,在controller层是使用util。
- html的meta用法
- How to change the text color in the terminal
- Swift基础学习(二)数据类型
- AlldayTest 产品使用--文件
- springboot jwt token前后端分离_基于Spring Boot+Spring Security+JWT+Vue前后端分离的开源项目...
- Factory Method (工厂模式)
- 计算机c盘是软盘吗,涨知识:为什么电脑都是从C盘开始,没有A和B?
- win10电脑系统里的视频不显示缩略图
- JDBC 常用的类和接口--一学就会(欢迎转载)
- 经济应用文写作【7】
- ferguson博弈_博弈 - xiaoguozi's Blog - C++博客
- MiniOSD DIY记
- 如何建设网站步骤有哪些?
- maxmemory-policy
- 指定服务器不存在或访问被拒绝访问,SQL Server不存在或访问被拒绝怎么办
- Unity IOS包在IPhone出现闪退
- P2P追债也用上大数据
- PHP学习线路图:四个阶段
热门文章
- 02.查询并杀死僵尸进程
- Android中的asserts和res/raw资源目录
- 可疑交易_IE的可疑版本定位
- 为什么我在2018年从Angular切换到React和Redux
- 大神note3千元指纹机,这是要逼疯友商吗
- 王启军:云原生架构下如何拆分微服务?
- 附代码 ConvNeXts:A ConvNet for the 2020s
- 2022年最佳电动汽车、最佳新车榜单:特斯拉、大众、梅赛德斯、日产等车型入选 | 美通社头条...
- Windows怎么查看苹果heic文件
- easyui edatagrid saveRow保存刷新