zoukankan      html  css  js  c++  java
  • [洛谷P3919]【模板】可持久化数组(可持久化线段树/平衡树)

    题目大意:

    你需要维护这样的一个长度为 N 的数组,支持如下几种操作

    1. 在某个历史版本上修改某一个位置上的值

    2. 访问某个历史版本上的某一位置的值

    此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),就会生成一个新的版本。版本编号即为当前操作的编号(从1开始编号,版本0表示初始状态数组)

    解题思路:很明显,本题需要用到可持久化数据结构。主席树即可。

    主要思路就是,维护线段树,对每个节点进行标号。

    这样做的好处是:由于之前版本的树是不会改变的,而本题只有单点查询,因此最多只会有$log_2 n$个节点的信息进行改变。进行标号以后,如果某棵子树没有改变,则直接将原来的编号保存下来即可,省时间又省空间。

    对于题目的第一种操作,只要保存历史版本根节点的编号即可(整棵树没变化),然后单点查询。

    对于题目的第二种操作,相当于单点修改,每次记录该节点的原编号和现编号,然后对于没有变化的一个子树就可以直接保存,有编化的修改即可。

    说一说此题坑我的地方:本题对于不同数据范围,空间分配是不一样的(最低128MB,最高512MB),而我直接开了数组,导致被卡空间MLE。使用指针分配内存的方式成功解决此问题(代码第55~59行)。

    C++ Code:

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #define N 1000005
    int n,m,rt[N],cnt,Q,p;
    int *ld,*rd,*d;
    inline int readint(){
    	char c=getchar();
    	bool b=false;
    	for(;!isdigit(c);c=getchar())b=c=='-';
    	int d=0;
    	for(;isdigit(c);c=getchar())d=(d<<3)+(d<<1)+(c^'0');
    	return b?-d:d;
    }
    inline void putint(int d){
    	if(d==0)putchar('0');else{
    		if(d<0)putchar('-'),d=-d;
    		int w=1;
    		for(;w<=d;w*=10);
    		for(w/=10;w;w/=10)
    		putchar((d/w%10)^'0');
    	}
    	putchar('
    ');
    }
    void build_tree(int l,int r,int o){
    	if(l==r){
    		d[o]=readint();
    		return;
    	}
    	int mid=(l+r)>>1;
    	build_tree(l,mid,ld[o]=++cnt);
    	build_tree(mid+1,r,rd[o]=++cnt);
    }
    void query(int l,int r,int o){
    	if(l==r)putint(d[o]);else{
    		int mid=(l+r)>>1;
    		if(Q<=mid)query(l,mid,ld[o]);else
    		query(mid+1,r,rd[o]);
    	}
    }
    void change(int l,int r,int old,int now){
    	if(l==r)d[now]=p;else{
    		int mid=(l+r)>>1;
    		if(Q<=mid){
    			rd[now]=rd[old];
    			change(l,mid,ld[old],ld[now]=++cnt);
    		}else{
    			ld[now]=ld[old];
    			change(mid+1,r,rd[old],rd[now]=++cnt);
    		}
    	}
    }
    int main(){
    	n=readint(),m=readint();
    	if(n<=150000){
    		d=new int[N<<2];ld=new int[N<<2];rd=new int[N<<2];
    	}else{
    		d=new int[N*20];ld=new int[N*20];rd=new int[N*20];
    	}
    	memset(rt,0,sizeof rt);
    	memset(ld,0,sizeof ld);
    	memset(rd,0,sizeof rd);
    	build_tree(1,n,rt[0]=cnt=1);
    	for(int i=1;i<=m;++i){
    		int v=readint(),opt=readint();
    		Q=readint();
    		if(opt==2)query(1,n,rt[i]=rt[v]);else{
    			rt[i]=++cnt;
    			p=readint();
    			change(1,n,rt[v],rt[i]);
    		}
    	}
    	return 0;
    }
    

      

  • 相关阅读:
    mySQL如何在查询的结果前添加序号
    bootstrap 列表前添加序号 1.10版本
    sql 如何优先显示不为空的字段 并进行排序
    java面向对象总结
    线程总结(二)
    数据库索引介绍(转载)
    线程总结(一)
    GUI图形界面编程之事件处理机制
    Eclipse快捷键大全(转载)
    JDBC数据库编程总结
  • 原文地址:https://www.cnblogs.com/Mrsrz/p/7910768.html
Copyright © 2011-2022 走看看