首页 > 编程语言 >算法笔记——可持久化线段树

算法笔记——可持久化线段树

时间:2024-08-16 19:04:29浏览次数:9  
标签:return int 线段 mid 笔记 算法 build ind root

  • 维护历史值

    当要修改一个节点时,把跟他有关的线段树中所有节点舍弃,并建立新节点连接.

    img

    代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1e6+5;
    int n,m,a[N],root[N],top;
    struct node
    {
    	int l,r,val;
    }t[N*40];
    int clone(int x)//新建节点
    {
    	top++;
    	t[top]=t[x];
    	return top;
    }
    int build(int x,int l,int r)//建树
    {
    	x=++top;
    	if(l==r)
    	{
    		t[x].val=a[l];
    		return top;
    	}
    	int mid=(l+r)>>1;
    	t[x].l=build(t[x].l,l,mid);
    	t[x].r=build(t[x].r,mid+1,r);
    	return x;
    }
    int update(int u,int l,int r,int x,int val){
    	u=clone(u);
    	if(l==r){
    		t[u].val=val;
    		return u;
    	}
    	else{
    		int mid=(l+r)>>1;
    		if(x<=mid)
    		{
    			t[u].l=update(t[u].l,l,mid,x,val);
    		}
    		else
    		{
    			t[u].r=update(t[u].r,mid+1,r,x,val);
    		}
    	}
        return u;
    }
    int query(int u,int l,int r,int x)
    {
    	if(l==r){
    		return t[u].val;
    	}
    	else{
    		int mid=(l+r)>>1;
    		if(x<=mid)return query(t[u].l,l,mid,x);
    		else return query(t[u].r,mid+1,r,x);
    	}
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    	root[0]=build(0,1,n);
    	for(int i=1,op,rt,x,y;i<=m;i++)
    	{
    		scanf("%d%d%d",&rt,&op,&x);
    		if(op==1)
    		{
    			scanf("%d",&y);
    			root[i]=update(root[rt],1,n,x,y);
    		}
    		else
    		{
    			printf("%d\n",query(root[rt],1,n,x));
    			root[i]=root[rt];
    		}
    	}
    	return 0;
    }
    
  • 求区间第k最值

    先离散化数组.

    再建立可持久化线段树,依次加入数组里每个数

    利用前缀和,求第k最值.

    #include <bits/stdc++.h>
    using namespace std;
    const int N=2e5+5;
    int n,ind[N],a[N],m,len,rt[N];
    int tot;
    int ls[N<<5],rs[N<<5],sum[N<<5];
    int getid(int x)
    {
    	return lower_bound(ind+1,ind+len+1,x)-ind;
    }
    int build(int l,int r)
    {
    	int root=++tot;
    	if(l==r)return root;
    	int mid=(l+r)>>1;
    	ls[root]=build(l,mid);
    	rs[root]=build(mid+1,r);
    	return root; 
    }
    int update(int k,int l,int r,int root)
    {
    	int dir=++tot;
    	ls[dir]=ls[root],rs[dir]=rs[root],sum[dir]=sum[root]+1;//多了一个数
    	if(l==r)
    	{
    		return dir;
    	}
    	int mid=(l+r)>>1;
    	if(k<=mid)
    	{
    		ls[dir]=update(k,l,mid,ls[dir]);
    	}
    	else
    	{
    		rs[dir]=update(k,mid+1,r,rs[dir]);
    	}
    	return dir;
    }
    int query(int u,int v,int l,int r,int k)
    {
    	int x=sum[ls[v]]-sum[ls[u]];//求左子树有多少种数
    	if(l==r)
    	{
    		return l;
    	}
    	int mid=(l+r)>>1;
    	if(k<=x)//在这个区间中,第k最值小于等于左子树数的总数
    	{
    		return query(ls[u],ls[v],l,mid,k);
    	}
    	else
    	{
    		return query(rs[u],rs[v],mid+1,r,k-x);
    	}
    }
    void init()
    {
    	cin>>n>>m;
    	for(int i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	memcpy(ind,a,sizeof(ind));
    	sort(ind+1,ind+n+1);
    	len=unique(ind+1,ind+n+1)-ind-1;//离散化
    	rt[0]=build(1,len);
    	for(int i=1;i<=n;i++)
    	{
    		rt[i]=update(getid(a[i]),1,len,rt[i-1]);
    	}
    }
    int l,r,k;
    void work()
    {
    	while(m--)
    	{
    		cin>>l>>r>>k;
    		cout<<ind[query(rt[l-1],rt[r],1,len,k)]<<'\n';
    	}
    }
    int main()
    {
    	init();
    	work();
    	return 0;
    }
    

标签:return,int,线段,mid,笔记,算法,build,ind,root
From: https://www.cnblogs.com/tyccyt/p/18363483

相关文章

  • 力扣面试经典算法150题:找出字符串中第一个匹配项的下标
    找出字符串中第一个匹配项的下标今天的题目是力扣面试经典150题中的数组的简单题:找出字符串中第一个匹配项的下标题目链接:https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/description/?envType=study-plan-v2&envId=top-interview-......
  • 统计学(贾俊平)学习笔记--第二章
    本章主要讲解了数据来源、调查方法、试验方法,以及数据抽样误差、非抽样误差,误差产生的原因等内容。该章内容较简单,不在仔细分析。这些也是数据来源的方法,大家可以了解。抽样误差(samplingerror)是由抽样的随机性引起的样本结果与总体真值之间的差异。影响抽样误差大小的因素......
  • KMP算法
    KMP算法介绍KMP(Knuth-Morris-Pratt)算法是一种高效的字符串匹配算法,用于在主串(text)中查找模式串(pattern)。KMP通过预处理模式串,避免了在匹配失败时回溯主串,提高了匹配效率。其时间复杂度为O(n+m),其中n是主串的长度,m是模式串的长度。KMP算法的关键思想KMP算法的核心思想......
  • 简单的linux系统学习笔记——01
    一、准备环境1.创建虚拟主机软件:vmware(是一个管理虚拟机的平台)vmware官网流程:1.点击创建新的虚拟机2.选择自定义3.兼容选最大4.选稍后安装操作系统5.选择想创建的系统6.命名,选择位置(保存位置)7.选择cpu和内存配置8.选nat模式9.选推荐10.创建新的虚拟磁盘11设置磁......
  • 简单的linux系统学习笔记——07
    一、虚拟机克隆本体删了,克隆机也会消失1.先关闭虚拟机2.右键虚拟机点管理,点击克隆3.克隆源选择“虚拟机中的当前状态”4.克隆类型选择“创建链接克隆”5.编辑虚拟机名字,设置存放路径6.点击完成7.修改主机名8.更改ip,修改网卡9.重启,保存快照二、文件文件属性:1)权限2......
  • Leetcode刷题笔记8.12-8.16
    Leetcode刷题笔记8.12-8.1619.删除倒数第n个链表结点(8.12)一个巧妙删除倒数第n个结点的trick该方法避免了对链表的一次全面扫描来获得总长度//返回链表的倒数第k个节点ListNodefindFromEnd(ListNodehead,intk){ListNodep1=head;//p1先走k步......
  • CRC算法原理、推导及实现
    CRC,CyclicRedundancyCheck,循环冗余校验1.基本原理CRC的本质是除法,把待检验的数据当作一个很大(很长)的被除数,两边选定一个除数(有的文献叫poly),最后得到的余数就是CRC的校验值。判定方法:将消息和校验和分开。计算消息的校验和(在附加W个零后),并比较两个校验和。把校验......
  • 加密算法
    加密算法概述加密算法是保护信息安全的重要工具,广泛应用于数据传输、存储和身份验证等领域。根据不同的目的和特性,加密算法可以分为几类。一、对称加密算法对称加密算法是指加密和解密使用相同的密钥。其优点是速度快,适合大数据量的加密,但密钥管理是一个挑战。1.DES(数据加密......
  • 算法模板
    拓扑排序点击查看代码publicBooleanbfsOptimize(intn,int[][]edges){//初始化入度数组,每个节点有两个元素,第一个元素表示入度,第二个元素暂时未使用int[][]indegrees=newint[n][2];//初始化邻接表,用于存储图的连接关系List<List<Integer>>adj......
  • 基于协同过滤推荐算法+springboot+vue的篮球球队网站
    博主主页:猫头鹰源码博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万+、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作​主要内容:毕业设计(Javaweb项目|小程序|Python|HTML|数据可视化|SSM|SpringBoot|Vue|Jsp|PHP......