zoukankan      html  css  js  c++  java
  • 可持久化数据结构

    先挖坑,过几天填

    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=2e5+10;
    int ls[maxn<<5],rs[maxn<<5],size[maxn<<5];
    int root[maxn];
    int nodecnt,n,m;
    int get(int rt){
    	nodecnt++;
    	ls[nodecnt]=ls[rt];
    	rs[nodecnt]=rs[rt];
    	size[nodecnt]=size[rt];
    	return nodecnt;
    }
    void insert(int &rt,int l,int r,int x,int val){
    	rt=get(rt);
    	size[rt]+=val;
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	if(x<=mid)insert(ls[rt],l,mid,x,val);
    	else insert(rs[rt],mid+1,r,x,val);
    }
    int ask(int u,int v,int l,int r,int k){
    	if(l==r)return l;
    	int mid=(l+r)>>1;
    	int x=size[ls[v]]-size[ls[u]];
    	if(x>=k)return ask(ls[u],ls[v],l,mid,k);
    	else return ask(rs[u],rs[v],mid+1,r,k-x);
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	root[0]=++nodecnt;
    	for(int i=1;i<=n;i++){
    		int x;scanf("%d",&x);
    		root[i]=root[i-1];
    		insert(root[i],-1e9,1e9,x,1);
    	}
    	for(int i=1;i<=m;i++){
    		int l,r,k;
    		scanf("%d%d%d",&l,&r,&k);
    		printf("%d
    ",ask(root[l-1],root[r],-1e9,1e9,k));
    	}
    }
    

    对离散化数组开

    #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]);//输出原数据
    	}
    	
    }
    

    例题

    P3168 [CQOI2015]任务查询系统
    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    const int maxn=6e5+5;
    int nodecnt;
    int tree[maxn<<5],ls[maxn<<5],rs[maxn<<5],size[maxn<<5],root[maxn];
    int pre=1;
    vector<int>a[maxn],b[maxn];
    int get(int rt){
    	nodecnt++;
    	ls[nodecnt]=ls[rt];
    	rs[nodecnt]=rs[rt];
    	size[nodecnt]=size[rt];
    	tree[nodecnt]=tree[rt];
    	return nodecnt;
    }
    void add(int &rt,int l,int r,int x,int val){
    	rt=get(rt);
    	size[rt]+=val;
    	tree[rt]+=val*x;
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	if(x<=mid)add(ls[rt],l,mid,x,val);
    	else add(rs[rt],mid+1,r,x,val);
    	return;
    }
    int query(int rt,int l,int r,int k){
    	if(l==r&&size[rt]!=0)return tree[rt]/size[rt]*k;
    	else if(l==r)return 0;
    	int mid=(l+r)>>1;
    	if(size[ls[rt]]>=k)return query(ls[rt],l,mid,k);
    	else return tree[ls[rt]]+query(rs[rt],mid+1,r,k-size[ls[rt]]);
    }
    signed main(){
    	int n,m;
    	scanf("%lld%lld",&n,&m);
    	for(int i=1;i<=n;i++){
    		int x,y,z;
    		scanf("%lld%lld%lld",&x,&y,&z);
    		a[x].push_back(z);
    		b[y+1].push_back(z);
    	}
    	for(int i=1;i<=m;i++){
    		root[i]=root[i-1];
    		for(int j=0;j<a[i].size();j++)add(root[i],1,1e7,a[i][j],1);
    		for(int j=0;j<b[i].size();j++)add(root[i],1,1e7,b[i][j],-1);
    	}
    	for(int i=1;i<=m;i++){
    		int x,a,b,c;
    		scanf("%lld%lld%lld%lld",&x,&a,&b,&c);
    		pre=query(root[x],1,1e7,1+(a*pre+b)%c);
    		printf("%lld
    ",pre);
    	}
    
    }
    

    可持久化平衡树

    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));
    		}
    	}
    	
    }
    
    

    可持久化并查集

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

    #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");
    		}
    	}
    }
    

    可持续化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));
    		}
    		
    	}
    }
    

    填坑完

  • 相关阅读:
    location.replace与location.href,location.reload的区别
    转载关于KeyPress和KeyDown事件的区别和联系
    Javascript中call的使用
    按值和按引用的比较
    理解cookie的path和domain属性
    HTML的快速写法:Emmet和Haml
    SVN标准命令
    linux常用命令
    Android4.0(Phone)拨号启动过程分析(一)
    Activity生命周期
  • 原文地址:https://www.cnblogs.com/soda-ma/p/13358503.html
Copyright © 2011-2022 走看看