首页 > 其他分享 >可持久化数组

可持久化数组

时间:2022-12-19 01:22:48浏览次数:63  
标签:right 持久 point int tree 数组 节点 left

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

写在前面

我是蒟蒻,不会区间修改,只知道要。

思想

可持久化数组要求修改之前的版本,所以我们必须保存之前的版本,但是对于每次都再建一颗线段树,空间是极大的,所以我们考虑优化。
我们修改图中的黄色节点,线段树从前一棵变为了后一棵。

注意到,图中的红色框中的节点是相同的,所以我们考虑重复利用节点。
下图展示了可持久化数组的结构。

图中的红色连边是重复利用节点的关键,我们注意到三个关键点:
\(1.\)图中不只有一个根节点。
\(2.\)每一个根节点都构成一棵线段树,都对应一个版本。
\(3.\)每一次修改只会增加\(\log_{2}n\)个节点。
所以现在思路就很明显了。
下面给出代码,对照代码理解。
\(Code:\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+5,INF=2e9+5;
int tot=0,rt[N];//tot为点数,rt[]为根节点编号
struct node{
	int l,r,val;//节点的储存,分别为:左子节点,右子节点,点权值
}tree[N<<5];//要注意细节,空间要开到20倍,所以至少位移5位
inline int read(){//读入优化
    int x=0;bool s=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')s=1;c=getchar();}
    while('0'<=c&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return s?-x:x;
}
int copy(int i){//复制节点,将下一个节点复制为tree[i]
	tree[++tot]=tree[i];
	return tot;
}
int build(int left,int right){//建树
	int i=++tot;//
	if(left==right){//到叶节点
		tree[i].val=read();//读入边权
		tree[i].l=tree[i].r=INF;//没有左右孩子
		return i;
	}
	int mid=left+right>>1;//递归建左右子树
	tree[i].l=build(left,mid);
	tree[i].r=build(mid+1,right);
	return i;
}
int update(int i,int left,int right,int x,int y){//修改操作
	int point=copy(i);//复制i节点
	if(left==right){//到根节点
		tree[point].val=y;//修改权值
		tree[point].l=tree[point].r=INF;
	}
	else{//没找到
		int mid=left+right>>1;//递归去找
		if(x<=mid)tree[point].l=update(tree[point].l,left,mid,x,y);
		else tree[point].r=update(tree[point].r,mid+1,right,x,y);
	}
	return point;
}
int query(int i,int left,int right,int x){//查询
	if(left==right)return tree[i].val;
	else{
		int mid=left+right>>1;
		if(x<=mid)return query(tree[i].l,left,mid,x);
		else return query(tree[i].r,mid+1,right,x);
	}
}
int main(){
	int n=read(),m=read();
	rt[0]=build(1,n);
	for(int i=1;i<=m;i++){
		int v=read(),op=read(),loc=read();
		if(op==1){
			int value=read();
			rt[i]=update(rt[v],1,n,loc,value);
		}
		else{
			printf("%d\n",query(rt[v],1,n,loc));
			rt[i]=rt[v];
		}
	}
	return 0;
}

标签:right,持久,point,int,tree,数组,节点,left
From: https://www.cnblogs.com/jd122/p/16991348.html

相关文章

  • C#中List〈string〉和string[]数组之间的相互转换
    原文链接:https://www.jb51.net/article/32390.htmstring[]strings={"a","b","c","abc"};List<string>list=newList<string>(strings);string[]strings2=......
  • 209. 长度最小的子数组
    209.长度最小的子数组力扣题目链接我的代码:错误的滑动窗口publicintminSubArrayLen(inttarget,int[]nums){intleft=0,right=0;int......
  • Java数组(08)稀疏数组
       红标列不打印,第一行为总计数量       ......
  • 【LeeCode】697. 数组的度
    【题目描述】给定一个非空且只包含非负数的整数数组 ​​nums​​,数组的 度 的定义是指数组里任一元素出现频数的最大值。你的任务是在 ​​nums​​​ 中找到与 ​​......
  • 链表与数组的区别
    原文链接:https://baijiahao.baidu.com/s?id=1743478279629141019物理存储结构不同链表与数组在计算机中存储元素采用不同的物理存储结构,数组是顺序存储结构,链表是链式......
  • 稀疏数组
    分析问题因为二维数组的很多默认值是0,因此记录了很多没有意义的数据解决:稀疏数组(记录有效的坐标)稀疏数组介绍1使用条件:当一个数组中大部分元素为0,或者为同一值的数组......
  • 简单认识指针数组与数组指针
    指针数组:指针数组就是一个存放指针的数组。//指针数组#include<stdio.h>intmain(){inta[5]={1,2,3,4,5};intb[]={2,3,4,5,6};intc[]=......
  • 【LeeCode】4. 寻找两个正序数组的中位数
    【题目描述】给定两个大小分别为 ​​m​​​ 和 ​​n​​​ 的正序(从小到大)数组 ​​nums1​​​ 和 ​​nums2​​。请你找出并返回这两个正序数组的 中位数 。......
  • 1764. 通过连接另一个数组的子数组得到一个数组
    1764.通过连接另一个数组的子数组得到一个数组题解:数据范围小,直接暴力双指针publicbooleancanChoose(int[][]groups,int[]nums){intn=groups.le......
  • 「双指针/kmp」通过连接另一个数组的子数组得到一个数组(力扣第1764题)
    本题为12月17日力扣每日一题题目来源:力扣第1764题题目tag:双指针kmp题面题目描述给你一个长度为n的二维整数数组groups,同时给你一个整数数组nums。你是否可以从n......