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

可持久化数组

主席树做即可。

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

可持久化并查集

可持久化并查集

主席树做即可。

要按秩合并。(路径压缩每次建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=;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=1e5+;
struct node{
int ls,rs;
int fa,dep;
}t[N*];
int tot;
int n,m;
int las;
int rt[*N];
void build(int &x,int l,int r){
x=++tot;
if(l==r) {
t[x].fa=l,t[x].dep=;
return ;
}
build(t[x].ls,l,mid);
build(t[x].rs,mid+,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+,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+,r,to,ff);
}
int find(int o,int to){
// cout<<" o "<<o<<" to "<<to<<endl;
int now=query(rt[o],,n,to);
if(t[now].fa==to) return now;
return find(o,t[now].fa);
}
int main(){
scanf("%d%d",&n,&m);
build(rt[],,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=;
int o=;
while(m--){
rd(op);
if(op==){
++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],,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],,n,t[y].fa);
// cout<<" lplplp "<<lp<<endl;
t[lp].dep++;
}
}
las=o;
}else if(op==){
++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("");
}else puts("");
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 ;
} }
int main(){
Miracle::main();
return ;
} /*
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=;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=5e5+;
const int U=2e9+;
const int P=1e9+;
const int inf=;
int n;
struct node{
int ls,rs,sz;
}t[*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+;
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+,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>=) 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+,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+,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<=) return kth(t[x].ls,l,mid,k);
else return kth(t[x].rs,mid+,r,d);
}
int pre(int x,int l,int r,int c){
if(l>=c||t[x].sz==) return -inf;
else if(l==r) return l;
else{
int ret=pre(t[x].rs,mid+,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==) 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+,r,c);
}
}
int main(){
scanf("%d",&n);
int st,op,x;
int o=;
while(n--){
rd(st),rd(op);rd(x);
x+=P;
++o;
rt[o]=rt[st];
switch(op){
case :ins(rt[o],rt[st],,U,x);break;
case :dele(rt[o],rt[st],,U,x);break;
case :printf("%d\n",rk(rt[o],,U,x)+);break;
case :{
int tmp=kth(rt[o],,U,x-P);
//cout<<" tmp "<<tmp<<" "<<tmp-1e9<<" "<<tmp-1e9-1<<endl;
printf("%d\n",tmp-P);
break;
}
case :{
int tmp=pre(rt[o],,U,x);
if(tmp>=&&tmp<=U){
printf("%d\n",tmp-P);
}
else printf("%d\n",tmp);//not find
break;
}
case :{
int tmp=bac(rt[o],,U,x);
if(tmp>=&&tmp<=U){
printf("%d\n",tmp-P);
}
else printf("%d\n",tmp);//not find
break;
}
}
//cout<<" num "<<o<<" : "<<" tot "<<tot<<" sz "<<rt[o]<<" "<<t[rt[o]].sz<<endl;
}
return ;
} }
int main(){
Miracle::main();
return ;
} /*
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=;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=+;
const int U=;
int n,m;
struct trie{
int ch[];
int sz;
}t[*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-];
for(reg i=U;i>=;--i){
int c=(v>>i)&;
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+;
}
}
int query(int l,int r,int v){
int y=l->=?rt[l-]:rt[],x=rt[r];
int ret=;
for(reg i=U;i>=;--i){
int c=(v>>i)&;
int d=t[t[x].ch[!c]].sz-t[t[y].ch[!c]].sz;
if(d){
ret+=(<<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==) ret=max(ret,v);
return ret;
}
int main(){
scanf("%d%d",&n,&m);
int x;
for(reg i=;i<=n;++i){
rd(x),s^=x,ins(i,s);
}
char ch[];int l,r;
int now=n;
while(m--){
scanf("%s",ch+);
//cout<<"ss "<<s<<endl;
switch(ch[]){
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 ;
} }
int main(){
Miracle::main();
return ;
} /*
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=;bool fl=false;
while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);
(fl==true)&&(x=-x);
}
namespace Miracle{
const int N=+;
struct trie{
int ch[];
int sz;
}t[*N*+*N];
int tot;
int df,dfn[N],fdfn[N],dfn2[N];
int fa[N][];
int dep[N];
int a[N];
int n,m;
struct node{
int nxt,to;
}e[*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=;j>=;--j){
if(dep[fa[x][j]]>=dep[y]) x=fa[x][j];
}
if(x==y) return x;
for(reg j=;j>=;--j){
if(fa[x][j]!=fa[y][j]) x=fa[x][j],y=fa[y][j];
}
return fa[x][];
}
void ins1(int x,int y,int v){
rt1[x]=++tot;
x=rt1[x];
for(reg i=;i>=;--i){
int c=(v>>i)&;
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+;
}
}
void dfs(int x,int d){
dep[x]=d;
dfn[x]=++df;fdfn[df]=x;
ins1(x,rt1[fa[x][]],a[x]);
for(reg i=hd[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa[x][]) continue;
fa[y][]=x;
dfs(y,d+);
}
dfn2[x]=df;
}
int query1(int y,int x,int v){
x=rt1[x];y=rt1[y];
int ret=;
for(reg i=;i>=;--i){
int c=(v>>i)&;
int d=t[t[x].ch[!c]].sz-t[t[y].ch[!c]].sz;
if(d){
ret+=(<<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-];
rt2[x]=++tot;
x=rt2[x];
for(reg i=;i>=;--i){
int c=(v>>i)&;
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+;
}
}
int query2(int y,int x,int v){
x=rt2[x];y=rt2[y];
int ret=;
for(reg i=;i>=;--i){
int c=(v>>i)&;
int d=t[t[x].ch[!c]].sz-t[t[y].ch[!c]].sz;
if(d){
ret+=(<<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=;i<=n;++i){
rd(a[i]);
}
int x,y;
for(reg i=;i<=n-;++i){
rd(x);rd(y);add(x,y);add(y,x);
}
dep[]=-;
dfs(,);
for(reg j=;j<=;++j){
for(reg i=;i<=n;++i){
fa[i][j]=fa[fa[i][j-]][j-];
}
} for(reg i=;i<=n;++i){
ins2(i,a[fdfn[i]]);
}
int op;
int z;
while(m--){
scanf("%d",&op);
if(op==){
rd(x);rd(y);
printf("%d\n",query2(dfn[x]-,dfn2[x],y));
}
else{
rd(x);rd(y);rd(z);
int anc=lca(x,y);
printf("%d\n",max(query1(fa[anc][],x,z),query1(fa[anc][],y,z)));
}
}
return ;
} }
int main(){
Miracle::main();
return ;
} /*
Author: *Miracle*
Date: 2018/11/23 16:08:01
*/

异或

可持久化用途

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

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

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

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

[学习笔记]可持久化数据结构——数组、并查集、平衡树、Trie树的更多相关文章

  1. POJ2513 【并查集+欧拉路径+trie树】

    题目链接:http://poj.org/problem?id=2513 Colored Sticks Time Limit: 5000MS   Memory Limit: 128000K Total ...

  2. [学习笔记]我们追过的神奇异或(Trie树系列)

    引言 刚学了\(Trie\)树,写篇博客巩固一下. 题目 首先安利一发\(Trie\)树模板 1.Phone List 2.The XOR largest pair 3.The xor-longest ...

  3. UVALive - 5031 Graph and Queries (并查集+平衡树/线段树)

    给定一个图,支持三种操作: 1.删除一条边 2.查询与x结点相连的第k大的结点 3.修改x结点的权值 解法:离线倒序操作,平衡树or线段树维护连通块中的所有结点信息,加个合并操作就行了. 感觉线段树要 ...

  4. matlab学习笔记(一)单元数组

    matlab学习笔记(一)单元数组 1.floor(x) :取最小的整数 floor(3.18)=3,floor(3.98)=3 ceil(x)  :取最大的整数 ceil(3.18)=4,ceil( ...

  5. python3.4学习笔记(十一) 列表、数组实例

    python3.4学习笔记(十一) 列表.数组实例 #python列表,数组类型要相同,python不需要指定数据类型,可以把各种类型打包进去#python列表可以包含整数,浮点数,字符串,对象#创建 ...

  6. Java学习笔记之---方法和数组

    Java学习笔记之---方法与数组 (一)方法 (1)什么是方法? 方法是解决一类问题的步骤的有序组合 方法包含于类或对象中 方法在程序中被创建,在其他地方被引用 (2)方法的优点 使程序变得更简短而 ...

  7. JavaSE学习笔记(7)---数组

    JavaSE学习笔记(7)---数组 1.什么是数组 数组是相同类型数据的有序集合.数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成.其中,每一个数据称作一个元素,每个元素可以通过一个 ...

  8. 「学习笔记」字符串基础:Hash,KMP与Trie

    「学习笔记」字符串基础:Hash,KMP与Trie 点击查看目录 目录 「学习笔记」字符串基础:Hash,KMP与Trie Hash 算法 代码 KMP 算法 前置知识:\(\text{Border} ...

  9. [BZOJ3038]上帝造题的七分钟2 树状数组+并查集

    考试的时候用了两个树状数组去优化,暴力修改,树状数组维护修改后区间差值还有最终求和,最后骗了40分.. 这道题有好多种做法,求和好说,最主要的是开方.这道题过的关键就是掌握一点:在数据范围内,最多开方 ...

随机推荐

  1. PHP中判断变量为空的几种方法小结

    isset  主要用来判断变量是否被初始化过empty  可以将值为 "假"."空"."0"."NULL"." ...

  2. windows环境下,用python绘图库matplotlib绘图时中文乱码问题

    1.下载中文字体(看自己爱好就行)下面这个举例: SimHei - Free Font Download​www.fontpalace.co 2.下载之后,打开即可安装,将字体安装进windows系统 ...

  3. C# 测量程序运行时间

    using System.Diagnostics; Stopwatch watch = new Stopwatch(); watch.Start(); /* 需要测量运行时间的程序 */ watch. ...

  4. linux下磁盘分区、格式化、挂载

    (1)fdisk /dev/sdb进行分区 (2)选择n表示添加一个分区,选择d表示删除一个分区.可通过m获取帮助信息 (3)选择p表示主分区,然后输入分区大小 (4)分区完成后,可通过fdisk - ...

  5. win7 下安装oracle 11g出现错误: 启动服务出现错误 找不到服务OracleMTSRecoveryService

    这种错误是在多次安装oracle都没有成功的情况下发生的. 正确安装oracle,是有前提条件的 1,安装最新的jdk,不是jre!!(并配好环境变量,在cmd中测试 java -version与ja ...

  6. intellij idea之git执行打标签(tag)和删除标签

    intellij idea 版本为2017.2.6 进入Version Control-->log 1.在之前版本中,右键,新建标签 2.输入标签名称,建议输入版本号的方式 3.push标签 由 ...

  7. wnds更新为1.0

    重画了外观,增加了若干功能,已经上传到码云 https://gitee.com/alan0405/wnds

  8. ASP NET Core ---REST & HTTP GET

    参照 草根专栏- ASP.NET Core + Ng6 实战:https://v.qq.com/x/page/h0764n405ll.html 一.REST (Representational Sta ...

  9. 分享 go语言爬虫---开源项目Pholcus

    写在开头的话:记录一下最近学习Pholcus(https://github.com/henrylee2cn/pholcus)的过程,首先去学习的go基本语法,在没接触的时候发现很多不理解的地方,但是当 ...

  10. python第一天(安装运行python)

    1. 安装Python 3.7 目前,Python有两个版本,一个是2.x版,一个是3.x版,这两个版本是不兼容的.由于3.x版越来越普及,我们的教程将以最新的Python 3.7版本为基础.请确保你 ...