一些树形数据结构模板(可持久化)
可持久化相关
可持久化数组
例题:维护一个数组,支持保存历史版本,支持在历史版本上修改、查询。洛谷 P3919
void insert(int &x,int u,int l,int r,int p,int k){
x=++tot;
tr[x]=tr[u],ls[x]=ls[u],rs[x]=rs[u];
if(l==r){
tr[x]=k;
return;
}
int mid=l+r>>1;
if(p<=mid)
insert(ls[x],ls[u],l,mid,p,k);
else insert(rs[x],rs[u],mid+1,r,p,k);
}
int query(int x,int l,int r,int p){
if(l==r)
return tr[x];
int mid=l+r>>1;
if(p<=mid)
return query(ls[x],l,mid,p);
return query(rs[x],mid+1,r,p);
}
非常简单且好写,注意每次 insert 都要 x=++tot;,以保证不会修改到历史版本。
可持久化权值线段树(主席树)
例题:静态区间第 \(k\) 小。洛谷 P3834
考虑全局第 \(k\) 小如何做。
显然,可以把所有数插进权值线段树,查询时在权值线段树上二分即可。
区间第 \(k\) 小也可以用类似的做法,考虑区间 \([l,r]\) 可以差分成 \([1,l-1],[1,r]\),查询时在两颗树上二分即可。
开 \(n\) 颗权值线段树显然会爆空间,可持久化即可。
void insert(int &x,int u,int l,int r,int k){
x=++cnt;
tr[x]=tr[u]+1,ls[x]=ls[u],rs[x]=rs[u];
if(l==r)
return;
int mid=l+r>>1;
if(k<=mid)
insert(ls[x],ls[u],l,mid,k);
else insert(rs[x],rs[u],mid+1,r,k);
}
int query(int x,int u,int l,int r,int k){
if(l==r)
return l;
int mid=l+r>>1;
if(tr[ls[u]]-tr[ls[x]]>=k)
return query(ls[x],ls[u],l,mid,k);
return query(rs[x],rs[u],mid+1,r,k-(tr[ls[u]]-tr[ls[x]]));
}
似乎没有什么实现上的难点。
可持久化并查集
例题:维护若干个集合,支持回退到历史版本,支持合并与查询。洛谷 P3402
发现合并两个集合 \(x,y\) 时,我们对于 \(fa\) 数组进行修改,因此本质上是维护可持久化数组。
注意不能路径压缩,考虑一个版本路径压缩后,在后续版本中不一定会被复用,多次修改 \(fa\) 数组导致时间开销过大。
因此考虑按秩合并,再维护一个 \(dep\) 数组即可。(具体实现可以封装可持久化数组)
struct tree{
int tr[N<<7],ls[N<<7],rs[N<<7],root[N],cnt;
void insert(int &x,int u,int l,int r,int p,int c){
x=++cnt;
tr[x]=tr[u],ls[x]=ls[u],rs[x]=rs[u];
if(l==r){
tr[x]=c;
return;
}
int mid=l+r>>1;
if(p<=mid)
insert(ls[x],ls[u],l,mid,p,c);
else insert(rs[x],rs[u],mid+1,r,p,c);
}
int query(int x,int l,int r,int p){
if(l==r)
return tr[x];
int mid=l+r>>1;
if(p<=mid)
return query(ls[x],l,mid,p);
return query(rs[x],mid+1,r,p);
}
int find(int x,int v){
int fa=query(v,1,n,x);
if(fa==x)
return x;
return find(fa,v);
}
}
可持久化平衡树
例题:维护一个数据结构,支持基于历史版本插入、删除、查询排名、查询第 \(k\) 小、求前驱后继。洛谷 P3835
考虑 FHQ-Treap 的可持久化,仅需在 split 和 merge 时新建节点即可,代码也不算难写。
int newnode(int k){
pri[++idx]=rnd();
siz[idx]=1,val[idx]=k;
return idx;
}
int clone(int x){
pri[++idx]=rnd();
siz[idx]=siz[x],val[idx]=val[x];
ls[idx]=ls[x],rs[idx]=rs[x];
return idx;
}
void pushup(int x){
siz[x]=siz[ls[x]]+siz[rs[x]]+1;
}
void split(int u,int k,int &x,int &y){
if(!u){
x=y=0;
return;
}
int p=clone(u);
if(k<val[p]){
y=p;
split(ls[u],k,x,ls[y]);
}else{
x=p;
split(rs[u],k,rs[x],y);
}
pushup(p);
}
int merge(int x,int y){
if(!x||!y)
return x+y;
int p;
if(pri[x]<pri[y]){
p=clone(x);
rs[p]=merge(rs[p],y);
}else{
p=clone(y);
ls[p]=merge(x,ls[p]);
}
pushup(p);
return p;
}
void insert(int &rt,int ort,int k){
int x,y,z;
split(ort,k,x,y);
z=newnode(k);
rt=merge(merge(x,z),y);
}
void del(int &rt,int ort,int k){
int x,y,z;
split(ort,k,x,y);
split(x,k-1,x,z);
if(z)
z=merge(ls[z],rs[z]);
rt=merge(merge(x,z),y);
}
int rk(int u,int k){
if(!u)
return 1;
if(k<=val[u])
return rk(ls[u],k);
return siz[ls[u]]+1+rk(rs[u],k);
}
int kth(int u,int k){
if(k<=siz[ls[u]])
return kth(ls[u],k);
if(k<=siz[ls[u]]+1)
return val[u];
return kth(rs[u],k-siz[ls[u]]-1);
}
浙公网安备 33010602011771号