可持久化线段树练习

先挖坑,过几天填

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\n",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\n",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\n",getnum(root[i],-1e9,1e9+1,x)+1);
		}
		if(op==4){
			printf("%lld\n",getk(root[i],-1e9,1e9+1,x));
		}
		if(op==5){
			int ans=getnum(root[i],-1e9,1e9+1,x);
			if(ans==0)printf("%d\n",-0x7fffffff);
			else printf("%lld\n",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\n",0x7fffffff);
			else printf("%lld\n",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\n",opuerysum(root[i],1,tail,x-1)+1);
		}
		if(op==4){
			printf("%d\n",tmp[opuerykth(root[i],1,tail,x)]);
		}
		if(op==5){
			printf("%d\n",opuerypre(i,x));
		}
		if(op==6){
			printf("%d\n",opuerysucc(i,x));
		}
	}
	
}

猜你喜欢

转载自www.cnblogs.com/soda-ma/p/13358503.html
今日推荐