首页 > 其他分享 >[OI] 可持久化数据结构

[OI] 可持久化数据结构

时间:2024-08-13 17:50:40浏览次数:13  
标签:cnt 持久 OI int mid tol return 数据结构 id

学了一年 OI 才看懂这句话:

\(\log n\) 是以什么为底的?

其实没什么区别

因为我们自动忽略常数,因此 \(\log_{a}n=\frac{\log_{x}n}{\log_{x}a}=\log_{x}n\)

可持久化数组

可持久化数组是基于可持久化线段树的一种数据结构. 它可以支持如下操作:

  • 单点修改
  • 查询历史版本
  • 在历史版本上修改

考虑到,为了实现这个功能,我们可以给每个历史版本都复制一份副本,但是这样的复杂度是 \(O(nt)\) 的

考虑优化空间复杂度:现在把整个数组放在线段树上,令数组为线段树的全部叶节点,因为我们每次修改节点最多只会带来 \(\log n\) 的改变,因此我们考虑在原树上仅仅改变这些点. 用动态开点思想可以很轻松地做到这一点.

比较水,直接放代码了

namespace HIST_Stree{
	const int N=1000000;
	int root[N];
	#define mid(l,r) mid=((l)+(r))/2
	struct tree{
		int tol,tor;
		int w;
	}t[30*N];
	#define tol t[id].tol
	#define tor t[id].tor
	int cnt=0;
	int newnode(){
		t[++cnt]={};
		return cnt;
	}
	int clone(int node){
		t[++cnt]=t[node];
		return cnt;
	}
	void build(int &id,int l,int r){
		id=newnode();
		if(l==r){
			t[id].w=a[l];
			return;
		}
		int mid(l,r);
		build(tol,l,mid);
		build(tor,mid+1,r);
	}
	void update(int &id,int l,int r,int pos,int val){
		id=clone(id);
		if(l==r){
			t[id].w=val;
			return;
		}
		int mid(l,r);
		if(pos<=mid) update(tol,l,mid,pos,val);
		else update(tor,mid+1,r,pos,val);
	}
	int ask(int id,int l,int r,int pos){
		if(l==r){
			return t[id].w;
		}
		else{
			int mid(l,r);
			if(pos<=mid) return ask(tol,l,mid,pos);
			else return ask(tor,mid+1,r,pos);
		}
	}
}

if(op==1){
	scanf("%d",&val);
	update(root[i]=root[t],1,n,pos,val);
}
else{
	printf("%d\n",ask(root[t],1,n,pos));
	root[i]=root[t];
}

可持久化线段树

可持久化线段树最经典的应用即为求区间第 \(k\) 小

先不考虑可持久化,考虑开一颗权值线段树,那么我们按照下述方法即可求出区间 \([1,r]\) 内的第 \(k\) 小:

先将原数组 \(a\) 排序离散化为 \(b\),将 \(a\) 中的元素依次插入权值线段树内. 每个节点插在位于 \(b\) 数组的位置上

为了方便统计答案,我们规定:插入节点时,途径节点的值加一

比如下列数组:

a: 1 5 2 6 3 7 4

排序后为

b: 1 2 3 4 5 6 7

图源

首先插入 \(a[1]=1\),其路径如下:

其次插入 \(a[2]=5\):

以此类推

最终会变成这样:

可以发现:因为我们开的是权值线段树,因此总是满足左区间小于右区间,因此要求 \([1,r]\) 的第 \(k\) 小,我们只需要考虑想二叉搜索树一样递归减掉 \(rank\) 即可.

那么对于求区间 \([l,r]\) 的问题怎么办呢?其实可以基于权值线段树有可减性的思想,直接用版本 \(r\) 减去版本 \(l-1\),剩下的就是在 \([l,r]\) 内的修改了.

实际实现的时候并不需要真的减出一颗树来,可以通过作差来做.

namespace HIST_Stree{
	#define mid(x,y) mid=((x)+(y))/2
	const int N=200001;
	int root[N],cnt=0;
	struct tree{
		int tol,tor;
		int sum;
	}t[N*32];
	#define tol(id) t[id].tol
	#define tor(id) t[id].tor
	int newnode(){
		t[++cnt]={};
		return cnt;
	}
	int clone(int node){
		t[++cnt]=t[node];
		return cnt;
	}
	void build(int &id,int l,int r){
		id=newnode();
		if(l==r){
			return;
		}
		int mid(l,r);
		build(tol(id),l,mid);
		build(tor(id),mid+1,r);
	}
	void change(int &id,int l,int r,int pos){
		id=clone(id);
		t[id].sum++;
		if(l==r) return;
		int mid(l,r);
		if(pos<=mid) change(tol(id),l,mid,pos);
		else change(tor(id),mid+1,r,pos);
	}
	int ask(int x,int y,int l,int r,int k){
		if(l==r) return l;
		int mid(l,r);
		if(t[t[y].tol].sum-t[t[x].tol].sum>=k){
			return ask(t[x].tol,t[y].tol,l,mid,k);
		}
		else{
			return ask(t[x].tor,t[y].tor,mid+1,r,k-(t[t[y].tol].sum-t[t[x].tol].sum));
		}
	} 
}
using namespace HIST_Stree;

    sort(b+1,b+n+1);
	int l=unique(b+1,b+n+1)-b-1;
	build(root[0],1,l);
	for(int i=1;i<=n;++i){
		change(root[i]=root[i-1],1,l,lower_bound(b+1,b+l+1,a[i])-b);
	}
	while(m--){
		int L,R,K;
		scanf("%d %d %d",&L,&R,&K);
		printf("%d\n",b[ask(root[L-1],root[R],1,l,K)]);
	}

标签:cnt,持久,OI,int,mid,tol,return,数据结构,id
From: https://www.cnblogs.com/HaneDaCafe/p/18357386

相关文章

  • 探索 Kubernetes 持久化存储之 Rook Ceph 初窥门径
    在Kubernetes生态系统中,持久化存储是支撑业务应用稳定运行的基石,对于维护整个系统的健壮性至关重要。对于选择自主搭建Kubernetes集群的运维架构师来说,挑选合适的后端持久化存储解决方案是关键的选型决策。目前,Ceph、GlusterFS、NFS、Longhorn和openEBS等解决方案已在业界......
  • [JOISC2017] Cultivation
    link。不是,怎么四方跑得飞快啊?成最优解了?有人会卡吗?鉴定为剪枝太多导致的。一个出发点不太一样的思路。假设上下左右各被操作了\(U,D,L,R\)次。我们考虑一个点\((x,y)\)不被感染的条件是初始时\([x-D,x+U]\times[y-R,y+L]\)这个矩形内没有任何感染点。考虑扣出所有中间......
  • 25版王道数据结构课后习题详细分析 第五章 树与二叉树 5.1 树的基本概念
     一、单项选择题————————————————————————————————————————解析:树是一种分层结构,它特别适合组织那些具有分支层次关系的数据。正确答案:D————————————————————————————————————————解......
  • NOI Linux VSCode使用指北
    NOILinuxVSCode使用指北安装NOILinux不是已经帮你做好这一步了吗?准备首先在这里对VSC的界面做一个介绍。1.终端VSC相对于其他的编辑器的优势是有一个非常直观的内置终端,这也让我们可以专心在这一个窗口内编辑和调试代码。召唤终端的快捷键是Ctrl+Shift+P!召唤终......
  • D42 2-SAT+二进制枚举 P3825 [NOI2017] 游戏
    视频链接: P3825[NOI2017]游戏-洛谷|计算机科学教育新生态(luogu.com.cn)//2-SAT+二进制枚举O(2^8*(n+m))#include<iostream>#include<cstring>#include<algorithm>usingnamespacestd;constintN=100005;inthead[N],to[N<<1],ne[N<<1],idx;......
  • office 交互库 Apache POI
      <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>${apache.poi.version}</version><exclusions><exclusion><artifactId>common......
  • # Android开发 - FileWriter 类写入文件解析
    FileWriter是什么FileWriter是一个用于将字符数据写入文件中的类。在Java中,它使得文件的写入操作变得简单直观FileWriter继承自OutputStreamWriter类,进一步继承自WriterFileWriter构造方法FileWriter(StringfileName):创建一个FileWriter对象,用于写入指定文件......
  • Android开发 - File类文件操作解析
    File是什么File类用于处理文件和目录。它允许你创建、删除、读取和写入文件。你可以用它来获取文件路径、检查文件是否存在、获取文件大小等。例如,Filefile=newFile(context.getFilesDir(),"example.txt");可以用来在应用的私有目录中创建一个名为example.txt的文件......
  • aof日志持久化
    aof日志持久化aof默认关闭,开启需要将redis.conf中appendonlyno,修改为appendonlyyes每当redis接受到会修改数据集的命令时,就会把命令追加到AOF文件里,当你重启Redis时,AOF文件里的命令会被重新执行一次,重建数据。键值对数据库,包含任意个非空数据库aof配置appendonlyno#......
  • P4155 [SCOI2015] 计划
    [SCOI2015]计划-洛谷核心思路注意到,可推出, 表示战士 走  步到达战士位置。若可以走到且r<终点则答案+ 然后再加上自己这个哨兵,和走回自己的一个哨兵即可。AC代码#include<bits/stdc++.h>usingnamespacestd;constintN=2e6+9;intgo[N][22]......