可持久化练习

Posted soda-ma

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可持久化练习相关的知识,希望对你有一定的参考价值。

先挖坑,过几天填

https://www.luogu.com.cn/training/14535#problems

可持久化数组

P3919 【模板】可持久化线段树 1(可持久化数组)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int nodecnt;
int tree[maxn*30],ls[maxn*30],rs[maxn*30];
int root[maxn],a[maxn];
int maketree(int rt,int l,int r){//建树
	rt=++nodecnt;
	if(l==r){
		tree[rt]=a[l];
		return nodecnt;
	}
	int mid=(l+r)>>1;
	ls[rt]=maketree(ls[rt],l,mid);
	rs[rt]=maketree(rs[rt],mid+1,r);
	return rt;
}
int get(int rt){//拷贝原节点信息
	nodecnt++;
	tree[nodecnt]=tree[rt];
	ls[nodecnt]=ls[rt];
	rs[nodecnt]=rs[rt];
	return nodecnt;
}
int update(int rt,int l,int r,int x,int val){
	rt=get(rt);
	if(l==r){
		tree[rt]=val;
	}else{
		int mid=(l+r)>>1;
		if(x<=mid)ls[rt]=update(ls[rt],l,mid,x,val);
		else rs[rt]=update(rs[rt],mid+1,r,x,val);
	}
	return rt;
}
int query(int rt,int l,int r,int x){//查询操作
	if(l==r)return tree[rt];
	int mid=(l+r)>>1;
	if(x<=mid)return query(ls[rt],l,mid,x);
	return query(rs[rt],mid+1,r,x);
}
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)w=-1;ch=getchar();}
   while(ch>=‘0‘&&ch<=‘9‘) s=s*10+ch-‘0‘,ch=getchar();
   return s*w;
}

int main(){
	int n,m;
	n=read(),m=read();
	for(int i=1;i<=n;i++)a[i]=read();
	root[0]=maketree(0,1,n);//建原始版本线段树
	for(int i=1;i<=m;i++){
		int rt,op,x;
		rt=read(),op=read(),x=read();
		if(op==1){
			int y;
			y=read();
			root[i]=update(root[rt],1,n,x,y);//构建新节点线段树的log(n)个节点
		}
		else{
			printf("%d
",query(root[rt],1,n,x));
			root[i]=root[rt];//建立新版本,当前版本和查询版本一致
		}
	}
} 

可持久化线段树

P3834 【模板】可持久化线段树 2(主席树)

#include<bits/stdc++.h>
using namespace std;
const int maxn=200010;
int p;
int nodecnt;//实现动态开点
int a[maxn],b[maxn],n,m;//a是原数组,b是离散化数组
int size[maxn<<5],root[maxn],rs[maxn<<5],ls[maxn<<5];//size记录节点大小,root记录每一棵树的根,ls-->左儿子,rs-->右儿子
void build(int &rt,int l,int r){//间一棵空树
//引用,顺便修改,实现动态开点
	rt=++nodecnt;
	if(l==r){return;}
	int mid=(l+r)>>1;
	build(ls[rt],l,mid);
	build(rs[rt],mid+1,r);
}
int get(int rt){//继承该节点原版本的全部信息
	nodecnt++;
	ls[nodecnt]=ls[rt];
	rs[nodecnt]=rs[rt];
	size[nodecnt]=size[rt]+1;
	return nodecnt;
}
int modify(int rt,int l,int r){//通过rt(上一棵树的根节点)构建当前树
	rt=get(rt);//继承
	if(l==r)return rt;//返回当前节点的编号
	int mid=(l+r)>>1;
	if(p<=mid)ls[rt]=modify(ls[rt],l,mid);//记录下面节点返回的编号
	else rs[rt]=modify(rs[rt],mid+1,r);
	return rt;//返回当前节点的编号
}
int query(int u,int v,int l,int r,int k){
	int ans;
	int mid=(l+r)>>1;
	int x=size[ls[v]]-size[ls[u]];//记录在l到r中左子树(l~mid)添加的数字的个数
	if(l==r)return l;//到叶子节点,返回答案
	if(x>=k)ans=query(ls[u],ls[v],l,mid,k);//大于则说明左子树添加的节点数大于k,那么区间第k小在左侧
	else ans=query(rs[u],rs[v],mid+1,r,k-x);//区间第k小在右侧
	return ans;
}
int main(){
	int l,r,k,q,ans;
    scanf("%d%d", &n, &m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		b[i]=a[i];//离散化数组
	}
	sort(b+1,b+1+n);//排序
	q=unique(b+1,b+n+1)-b-1;//去重
	build(root[0],1,q);//构建空树
	for(int i=1;i<=n;i++){
		p=lower_bound(b+1,b+q+1,a[i])-b;//查找a[i]离散化后的大小(在b数组中的位置)
		root[i]=modify(root[i-1],1,q);//根据第i-1棵线段树构建第i棵线段树
	}
	while(m--){
		int l,r,k;
		scanf("%d%d%d",&l,&r,&k);
		int ans=query(root[l-1],root[r],1,q,k);//查询l,r中的区间k小
		//和前缀和类似,从树l-1到r查询
		printf("%d
",b[ans]);//输出原数据
	}
	
}

可持久化平衡树

P3835 【模板】可持久化平衡树
(维护历史版本的修改查询,这里提供线段树做法)

对值域开+动态开点

调了好长时间,一直28分,后来发现,竟然可以删除不存在的节点????(大雾)-->>加判断

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=5e5+10;
int size[maxn*30];
int ls[maxn*30],rs[maxn*30],root[maxn];
int nodecnt;
int get(int rt){
	++nodecnt;
	ls[nodecnt]=ls[rt];
	rs[nodecnt]=rs[rt];
	size[nodecnt]=size[rt];
	return nodecnt;
}
void add(int &rt,int l,int r,int x,int v){
	if(rt!=0)rt=get(rt);
	else if(rt==0)rt=++nodecnt;
	size[rt]+=v;
	if(l==r){
		if(size[rt]<0)size[rt]=0;	
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid)add(ls[rt],l,mid,x,v);
	else add(rs[rt],mid+1,r,x,v);
	size[rt]=size[ls[rt]]+size[rs[rt]];
}
int getnum(int rt,int l,int r,int x){
	if(size[rt]==0||x>r){
		return size[rt];
	}
	int mid=(l+r)>>1;
	if(x<=mid)return getnum(ls[rt],l,mid,x);
	return size[ls[rt]]+getnum(rs[rt],mid+1,r,x);
}
int getk(int rt,int l,int r,int k){
	if(l==r)return l;
	int mid=(l+r)>>1;
	if(size[ls[rt]]>=k)return getk(ls[rt],l,mid,k);
	return getk(rs[rt],mid+1,r,k-size[ls[rt]]);
}
signed main(){
	int n;
	scanf("%lld",&n);
	root[0]=++nodecnt;
	for(int i=1;i<=n;i++){
		int rt,op,x;
		scanf("%lld%lld%lld",&rt,&op,&x);
		root[i]=root[rt];
		if(op==1){
			add(root[i],-1e9,1e9+1,x,1);
		}
		if(op==2){
			add(root[i],-1e9,1e9+1,x,-1);
		}
		if(op==3){
			printf("%lld
",getnum(root[i],-1e9,1e9+1,x)+1);
		}
		if(op==4){
			printf("%lld
",getk(root[i],-1e9,1e9+1,x));
		}
		if(op==5){
			int ans=getnum(root[i],-1e9,1e9+1,x);
			if(ans==0)printf("%d
",-0x7fffffff);
			else printf("%lld
",getk(root[i],-1e9,1e9+1,ans));
		}
		if(op==6){
			int ans=getk(root[i],-1e9,1e9+1, getnum(root[i],-1e9,1e9+1,x +1)+1);
			if(ans==1e9+1)printf("%d
",0x7fffffff);
			else printf("%lld
",ans);
		}
	}

}

对数组开+离线操作+离散化

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
int nodecnt;
struct node{
	int v,op,x;
}temp[maxn];
int tmp[maxn],tail;
int rs[maxn*32],ls[maxn*32],size[maxn*32],root[maxn];
int n;

void build(int &curr, int l, int r) {
    curr = ++nodecnt;
    if (l == r) return;
    int mid = (l + r) >> 1;
    build(ls[curr], l, mid);
    build(rs[curr], mid + 1, r);
}
int get(int rt){
	nodecnt++;
	ls[nodecnt]=ls[rt];
	rs[nodecnt]=rs[rt];
	size[nodecnt]=size[rt];
	return nodecnt;
}
void add(int &rt,int l,int r,int x,int v){
	rt=get(rt);
	if(l==r){
		size[rt]+=v;
		if(size[rt]<0)size[rt]=0;
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid)add(ls[rt],l,mid,x,v);
	else add(rs[rt],mid+1,r,x,v);
	size[rt]=size[ls[rt]]+size[rs[rt]];
}

int opuerysum(int rt,int l,int r,int x){
	if(x<l)return 0;
	if(x>=r)return size[rt];
	int mid=(l+r)>>1;
	return opuerysum(ls[rt],l,mid,x)+opuerysum(rs[rt],mid+1,r,x);
}

int opuerykth(int rt,int l,int r,int k){
	if(l==r)return l;
	int mid=(l+r)>>1;
	if(k<=size[ls[rt]])return opuerykth(ls[rt],l,mid,k);
	else return opuerykth(rs[rt],mid+1,r,k-size[ls[rt]]);
}

int opuerypre(int v,int x){
	int ans=opuerysum(root[v],1,tail,x-1);
	if(!ans)return -2147483647;
	else return tmp[opuerykth(root[v],1,tail,ans)];

}
int opuerysucc(int v,int x){
	int ans=opuerysum(root[v],1,tail,x);
	if(ans>=size[root[v]])return 2147483647;
	else return tmp[opuerykth(root[v],1,tail,ans+1)];
}
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d%d",&temp[i].v,&temp[i].op,&temp[i].x);
		if(temp[i].op!=4){
			tmp[++tail]=temp[i].x;
		}
	}
	sort(tmp+1,tmp+tail+1);
	tail=unique(tmp+1,tmp+tail+1)-(tmp+1);
	build(root[0],1,tail);
	for(int i=1;i<=n;i++){
		int v=temp[i].v,op=temp[i].op,x=temp[i].x;
		if(op!=4){x=lower_bound(tmp+1,tmp+tail+1,x)-tmp;}
		root[i]=root[v];
		if(op==1){	
			add(root[i],1,tail,x,1);
		}
		if(op==2){
			add(root[i],1,tail,x,-1);
		}
		if(op==3){
			printf("%d
",opuerysum(root[i],1,tail,x-1)+1);
		}
		if(op==4){
			printf("%d
",tmp[opuerykth(root[i],1,tail,x)]);
		}
		if(op==5){
			printf("%d
",opuerypre(i,x));
		}
		if(op==6){
			printf("%d
",opuerysucc(i,x));
		}
	}
	
}

可持续化Trie

P4735 最大异或和

#include<bits/stdc++.h>
using namespace std;
const int N=6e5+10;
int nodecnt;
int tree[N*24][2],latest[N*24];
int s[N],root[N],n,m;
void get(int rt,int last){
	tree[rt][1]=tree[last][1];
	tree[rt][0]=tree[last][0];
	latest[rt]=latest[last];
}
void insert(int rt,int last,int i,int k){
	get(rt,last);
	if(k<0){
		latest[rt]=i;
		return;
	}
	bool rel=s[i]>>k&1;
	tree[rt][rel]=++nodecnt;
	insert(tree[rt][rel],tree[last][rel],i,k-1);
	latest[rt]=max(latest[tree[rt][0]],latest[tree[rt][1]]);
}
int ask(int rt,int val,int k,int lim){
	if(k<0)return s[latest[rt]]^val;
	int rel=val>>k&1;
	if(latest[tree[rt][rel^1]]>=lim)
		return ask(tree[rt][rel^1],val,k-1,lim);
	else 
		return ask(tree[rt][rel],val,k-1,lim);
}
int main(){
	scanf("%d%d",&n,&m);
	latest[0]=-1;
	root[0]=++nodecnt;
	insert(root[0],0,0,23);
	for(int i=1;i<=n;i++){
		int x;scanf("%d",&x);
		s[i]=s[i-1]^x;
		root[i]=++nodecnt;
		insert(root[i],root[i-1],i,23);
	}
	for(int i=1;i<=m;i++){				
		char op;
		scanf(" %c",&op);
		if(op==‘A‘){
			int x;scanf("%d",&x);
			root[++n]=++nodecnt;
			s[n]=s[n-1]^x;
			insert(root[n],root[n-1],n,23);
		}
		else{
			int l,r,x;
			scanf("%d%d%d",&l,&r,&x);
			printf("%d
",ask(root[r-1],x^s[n],23,l-1));
		}
		
	}
}

可持久化并查集

可持久化数组维护可持久化并查集??

#include<bits/stdc++.h>
using namespace std;
const int maxn=200050;
int n,m,nodecnt;
int dep[maxn*30],fa[maxn*30],root[maxn],rs[maxn*30],ls[maxn*30];
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)w=-1;ch=getchar();}
   while(ch>=‘0‘&&ch<=‘9‘) s=s*10+ch-‘0‘,ch=getchar();
   return s*w;
}
void build(int &rt,int l,int r){
	if(!rt)rt=++nodecnt;
	if(l==r){fa[rt]=l;return;}
	int mid=(l+r)>>1;
	build(ls[rt],l,mid);build(rs[rt],mid+1,r);
}
void update(int &rt,int pre,int l,int r,int x,int y){
	rt=++nodecnt;
	if(l==r){
		dep[rt]=dep[pre];
		fa[rt]=y;
		return;
	}
	ls[rt]=ls[pre],rs[rt]=rs[pre];
	int mid=(l+r)>>1;
	if(x<=mid)update(ls[rt],ls[pre],l,mid,x,y);
	else update(rs[rt],rs[pre],mid+1,r,x,y);
}
int query(int rt,int l,int r,int x){
    if(l==r)return rt;
    int mid=(l+r)>>1;
    if(x<=mid)return query(ls[rt],l,mid,x);
    else return query(rs[rt],mid+1,r,x);
}
void add(int rt,int l,int r,int x){
	if(l==r){dep[rt]++;return;}
	int mid=(l+r)>>1;
	if(x<=mid)add(ls[rt],l,mid,x);
	else add(rs[rt],mid+1,r,x);
}

int find_root(int rt,int now){
	int nowx=query(rt,1,n,now);
	if(fa[nowx]==now)return nowx;
	return find_root(rt,fa[nowx]);
}
int main(){
	n=read(),m=read();
	build(root[0],1,n);
	for(int i=1;i<=m;i++){
		int op;
		op=read();
		if(op==1){
			root[i]=root[i-1];
			int x=read(),y=read();
			int fx=find_root(root[i],x),fy=find_root(root[i],y);
			if(fa[fx]==fa[fy])continue;
			if(dep[fx]>dep[fy])swap(fx,fy);
			update(root[i],root[i-1],1,n,fa[fx],fa[fy]);
			if(dep[fx]+1>dep[fy])add(root[i],1,n,fa[fy]);
		}
		if(op==2){
			int kx=read();
			root[i]=root[kx];
		}
		if(op==3){
			root[i]=root[i-1];
			int x=read(),y=read();
			int fx=find_root(root[i],x),fy=find_root(root[i],y);
			if(fa[fx]==fa[fy])puts("1");
			else puts("0");
		}
	}
}

填坑完

技术图片


以上是关于可持久化练习的主要内容,如果未能解决你的问题,请参考以下文章

持久片段和查看器

片段中的Firebase数据不是持久的,会重新下载

SpringCloud系列十一:SpringCloudStream(SpringCloudStream 简介创建消息生产者创建消息消费者自定义消息通道分组与持久化设置 RoutingKey)(代码片段

COGS 2554. [福利]可持久化线段树

spring练习,在Eclipse搭建的Spring开发环境中,使用set注入方式,实现对象的依赖关系,通过ClassPathXmlApplicationContext实体类获取Bean对象(代码片段

AC日记——[福利]可持久化线段树 cogs 2554