数据结构——ST表

ST表的定义

ST表,又名稀疏表,是一种基于倍增思想,用于解决可重复贡献问题的数据结构

倍增思想

这里列举一个去寻找一个区间内的最大值的例子

 

 

因为每次会将将区间增大一倍,所以才被称之为倍增思想 ,这种思想十分好用,建议友友们平常多多练习

ST表的适用范围

如果A区间和B区间可能有重叠的部分
但是并不影响A+B区间的答案,能通过 A区间答案 和 B区间答案 就加工出来
那么对应的区间询问,就是一个可重复贡献问题
例如:区间最大值,区间最小值、区间公约数等,但是区间求和就不符合这个要求
再例如:区间按位与、区间按位或,ST表都能高效地解决


ST表的优势和劣势

 RMQ问题(Range Maximum/Minimum Query)可以用ST表维护,也可以用线段树等结构维护
ST表的优势:构建过程时间复杂度0(n*logn),单次查询时间复杂度0(1),代码量较小
ST表的劣势:需要空间较大,能维护的信息非常有限,不支持修改操作

只能维护静态区间内的可重复贡献类问题,不能维护动态的

模版

预处理模版:

//st[i][j]表示的是以i为起点,长度为2^j的区间的最值
//外层循环遍历的是长度的指数,内层循环遍历的是起点 
for(int j=1;j<20;j++)
{
	for(int i=1;i+(1<<j)-1<=n;i++)
	{
		st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
	}
} 

查询模版:

//maxn是统计给定区间的最值
//k是用来判断区间的长度
//l,r是给定的区间范围 
for(int i=1;i<=m;i++)
{
	cin>>l>>r;
	int k=log2(r-l+1);
	maxn=max(dp[l][k],dp[r-(1<<k)+1][k]);
} 

例题:

P3865 【模板】ST 表 && RMQ 问题

 

思路:十分板正的一个ST表板题,就是去求区间内的一个最大值,纯板题

#include<bits/stdc++.h>  
using namespace std;  
#define int long long  

int n, m;  
int l, r;  
int st[100005][20];  

signed main() {  
    ios::sync_with_stdio(0);  
    cin.tie(0);  
    cout.tie(0);  
    cin >> n >> m;  
    
    for (int i = 1; i <= n; i++) {  
        cin >> st[i][0];  
    }  
    for (int j = 1; j < 20; j++) {  
        for (int i = 1; i + (1 << j) - 1 <= n; i++) {  
            st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);  
        }  
    }  
    for (int i = 1; i <= m; i++) {  
        cin >> l >> r;  
        int k = log2(r - l + 1);  
        cout << max(st[l][k], st[r - (1 << k) + 1][k]) << "\n";  
    }  

    return 0;  
}

 P2880 [USACO07JAN] Balanced Lineup G

 思路:也是ST表的板题,只不过是去求一遍最大值的ST表,然后再去求一遍最小值的ST表,然后减去就可以了

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,q;
int f_max[50005][17];
int f_min[50005][17];
int l,r;
signed main()
{
	cin>>n>>q;
	for(int i=1;i<=n;i++)
	{
		cin>>f_max[i][0];
		f_min[i][0]=f_max[i][0];
	}
	for(int j=1;j<=16;j++)
	{
		for(int i=1;i+(1<<j)-1<=n;i++)
		{
			f_max[i][j]=max(f_max[i][j-1],f_max[i+(1<<(j-1))][j-1]);
			f_min[i][j]=min(f_min[i][j-1],f_min[i+(1<<(j-1))][j-1]);
		}
	}
	for(int i=1;i<=q;i++)
	{
		cin>>l>>r;
		int k=log2(r-l+1);
		cout<<max(f_max[l][k],f_max[r-(1<<k)+1][k])-min(f_min[l][k],f_min[r-(1<<k)+1][k])<<"\n";
	}
}

P2251 质量检测

思路1:滑动窗口问题,可以直接单调队列解决,去寻找m区间长度的窗口内的最小值,但是和今天的主题不相符,这种写法就靠看官自己去琢磨了

思路2:ST表,这题是求区间内的最小值,但是却是给你规定了区间大小,让你找出整个区间的这么大小的最小值,直接预处理一遍,然后遍历即可,但是如果m不是2^k,就找到最大的k, 然后用m-(2^k)+1的k区间长度,去找最小值即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
int st[1000005][25]; 
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>st[i][0];
	}
	for(int j=1;j<=21;j++)
	{
		for(int i=1;i+(1<<j)-1<=n;i++)
		{
			st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
		}
	}
	int k=log2(m);
	for(int i=1;i+m-1<=n;i++)
	{
		cout<<min(st[i][k],st[i+m-(1<<k)][k])<<"\n";
	}
	return 0;
}

P1890 gcd区间

思路:区间gcd也是一种可重复贡献问题,因此也可以用ST表,我们直接预处理一遍,然后直接跑即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
int st[1005][15];
int l,r;
int gcd(int a,int b)
{
	if(b==0)
	return a;
	return gcd(b,a%b);
}
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>st[i][0];
	}
	for(int j=1;j<=14;j++)
	{
		for(int i=1;i+(1<<j)-1<=n;i++)
		{
			st[i][j]=gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]);
		}
	}
	for(int i=1;i<=m;i++)
	{
		cin>>l>>r;
		int k=log2(r-l+1);
		cout<<gcd(st[l][k],st[r-(1<<k)+1][k])<<"\n";
	}
	return 0;
}

P4155 [SCOI2015] 国旗计划

思路:这题其实一开始我也没看出来是ST表的题目,后来想了好久我才恍然大悟,我们首先来看题,每个士兵都有自己负责的一个区域,为L和R,然后呢,我们有m个边防站,然后我们要遍历第i个士兵必须参与的情况下,最少需要多少个哨兵

我们首先需要拆环为链,我们之间将这个数组拷贝一遍,再延伸出来一个长度,然后我们去处理士兵负责的范围,加入右区间的端点,小于左区间端点,那么我们就直接将右区间的端点+m即可,然后我们再去复制每个哨兵一次,其区间的值都+m

然后我们就跑ST表,表示从i开始往后2^j次方能够达到什么位置,如果是当前这个人的左区间+m的位置,那么就说明覆盖了整个哨所,那就是可以了,我们需要去处理一下

然后直接写即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
int l,r;
struct node{
	int l;
	int r;
	int pos;
}a[400005];
int st[400005][25];
int sum[400004];
bool cmp(node a,node b)
{
	return a.l<b.l;
}
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>l>>r;
		if(l>r)
		{
			r+=m;
		}
		a[i].l=l;
		a[i].r=r;
		a[i].pos=i;
	}
	sort(a+1,a+1+n,cmp);
	for(int i=1;i<=n;i++)
	{
		a[i+n].l=a[i].l+m;
		a[i+n].r=a[i].r+m;
	}
	for(int i=1,j=1;i<=2*n;i++)
	{
		while(j<=2*n&&a[j].l<=a[i].r)
		{
			j++;
		}
		st[i][0]=j-1;
	}
	for(int j=1;j<=20;j++)
	{
		for(int i=1;i+(1<<j)-1<=2*n;i++)
		{
			st[i][j]=st[st[i][j-1]][j-1];
		}
	}
	for(int i=1;i<=n;i++)
	{
		int flag=a[i].l+m;
		int ans=0;
		int p=i;
		for(int j=20;j>=0;j--)
		{
			if(st[p][j]&&a[st[p][j]].r<flag)
			{
				ans+=1<<j;
				p=st[p][j];
			}
		}
		sum[a[i].pos]=ans+2;
	}
	for(int i=1;i<=n;i++)
	{
		cout<<sum[i]<<" ";
	}
	return 0;
}

 Frequent values

 

思路:我们应当用到题目说的有序,我们可以将每一种数视为一个桶,然后将每个桶内的个数去求出来,然后去记录每个桶的左端点和右端点,然后我们在询问的时候,然后我们去预处理桶的数目的ST表,表示从第i个桶开始长度为2^j次方的桶内的最大值,然后我们再去找两边的最大值,然后去处理三部分最大值的最大值即可

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,q;
int a;
int l[500005];
int r[500005];
int st[500005][24];
int num[500005];
int x,y;
void ini()
{
	memset(l,0,sizeof(l));
	memset(r,0,sizeof(r));
	memset(st,0,sizeof(st));
	memset(num,0,sizeof(num));
}
signed main()
{
	while(cin>>n)
	{
		if(n==0)
		{
			break;
		}
		ini();
		cin>>q;
		int len=0;
		int flag=-0x3f3f3f3f;
		for(int i=1;i<=n;i++)
		{
			cin>>a;
			if(a!=flag)
			{
				r[len]=i-1;
				len++;
				l[len]=i;
				flag=a;
			}
			st[len][0]++;
			num[i]=len;
		}
		r[len]=n;
		for(int j=1;j<=20;j++)
		{
			for(int i=1;i+(1<<j)-1<=n;i++)
			{
				st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
			}
		}
		for(int i=1;i<=q;i++)
		{
			cin>>x>>y;
			if(x>y)
			swap(x,y);
			if(num[x]==num[y])
			{
				cout<<y-x+1<<"\n";
			}
			else
			{
				int ans=0;
				int L=num[x]+1;
				int R=num[y]-1;
				if(L<=R)
				{
					int k=0;  
				    while((1<<(k+1))<=R-L+1) k++;
					ans=max(ans,max(st[L][k],st[R-(1<<k)+1][k]));
				}
				ans=max(ans,max(y-l[num[y]]+1,r[num[x]]-x+1));
				cout<<ans<<"\n";
			}
		}
	}
	return 0;
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/937438.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深入了解IPv6——光猫相关设定:DNS来源、DHCPv6服务、前缀来源等

光猫IPv6设置后的效果对比图&#xff1a; 修改前&#xff1a; 修改后&#xff1a; 一、DNS来源 1. 网络连接 来源&#xff1a; 从上游网络&#xff08;如运营商&#xff09;获取 IPv6 DNS 信息&#xff0c;通过 PPPoE 或 DHCPv6 下发。 特点&#xff1a; DNS 服务器地址直…

配置mysqld(读取选项内容,基本配置),数据目录(配置的必要性,目录下的内容,具体文件介绍,修改配置)

目录 配置mysqld 读取选项内容 介绍 启动脚本 基本配置 内容 端口号 数据目录的路径 配置的必要性 配置路径 mysql数据目录 具体文件 修改配置时 权限问题 配置mysqld 读取选项内容 介绍 会从[mysqld] / [server] 节点中读取选项内容 优先读取[server] 虽然服务…

我们需要什么样的运维:以业务目标为导向的运维体系建设

在数字化转型的浪潮中&#xff0c;运维作为信息技术基础设施的重要支撑&#xff0c;其重要性日益凸显。然而&#xff0c;传统的运维模式往往局限于网络稳定、设备监控和系统可用等基础目标&#xff0c;难以满足现代企业对业务支持的更高要求。那么&#xff0c;我们究竟需要什么…

docker 部署 redis

docker 部署 redis 1. 下载 redis 镜像 # docker images | grep redis bitnami/redis 7.2.4-debian-11-r5 45de196aef7e 10 months ago 95.2MB2. docker-compose 部署 version: "3" services:redis:image: bitnami/redis:7.2.4-debian-11-…

python学opencv|读取图像(十三)BGR图像和HSV图像互相转换深入

【1】引言 前序学习过程中&#xff0c;我们偶然发现&#xff1a;如果原始图像是png格式&#xff0c;将其从BGR转向HSV&#xff0c;再从HSV转回BGR后&#xff0c;图像的效果要好于JPG格式。 文章链接为&#xff1a; python学opencv|读取图像&#xff08;十二&#xff09;BGR图…

Python | 数据可视化中常见的4种标注及示例

在Python的数据可视化中&#xff0c;标注&#xff08;Annotation&#xff09;技术是一种非常有用的工具&#xff0c;它可以帮助用户更准确地解释图表中的数据和模式。在本文中&#xff0c;将带您了解使用Python实现数据可视化时应该了解的4种标注。 常见的标注方式 文本标注箭…

【机器学习】在向量的流光中,揽数理星河为衣,以线性代数为钥,轻启机器学习黎明的瑰丽诗章

文章目录 线性代数入门&#xff1a;机器学习零基础小白指南前言一、向量&#xff1a;数据的基本单元1.1 什么是向量&#xff1f;1.1.1 举个例子&#xff1a; 1.2 向量的表示与维度1.2.1 向量的维度1.2.2 向量的表示方法 1.3 向量的基本运算1.3.1 向量加法1.3.2 向量的数乘1.3.3…

基于 JNI + Rust 实现一种高性能 Excel 导出方案(下篇)

衡量一个人是否幸福&#xff0c;不应看他有多少高兴的事&#xff0c;而应看他是否为小事烦扰。只有幸福的人&#xff0c;才会把无关痛痒的小事挂心上。那些真正经历巨大灾难和深重痛苦的人&#xff0c;根本无暇顾及这些小事的。因此人们往往在失去幸福之后&#xff0c;才会发现…

Cesium中实现仿ArcGIS三维的动态图层加载方式

Cesium 加载 ArcGIS 动态图层的方式 如果你在 Cesium 中加载过 ArcGIS 的动态图层&#xff0c;你会发现&#xff0c;Cesium 对于动态图层仍然采用类似切片图层的逻辑进行加载。也就是每个固定的瓦片 export 一张图片。 这样会造成一些问题&#xff1a; 请求量大&#xff0c;…

信号处理:概念、技术、领域

目录 基本概念 主要技术 应用领域 信号处理是一个涉及分析、修改和再生信号的多学科领域。信号可以是各种形式的&#xff0c;例如声音、图像、视频或其他类型的监测数据。信号处理的主要目标是提取有用的信息并增强信号的质量。以下是信号处理的一些基本概念和应用&#xff…

【Redis】Redis 生成唯一 id

每个订单业务都需要有一个唯一的id&#xff0c;如果使用数据库自增id就会暴露规律&#xff0c;同时id会有一个最大的阈值&#xff0c;万一订单超过这个阈值&#xff0c;那就会出现问题。因此我们可以封装一个全局ID生成器&#xff0c;可以适用于分布式系统生成唯一ID&#xff0…

购物商城案例 -- VueCli创建项目,调整目录,vant组件库

基于VueCli创建项目 调整目录&#xff0c;新增两个目录 修改路由和App.vue 路由中规则清空 新建文件夹api和utils api文件夹&#xff1a;发请求的一些文件 utils文件夹&#xff1a;工具函数方法 vant组件库&#xff1a;第三方vue组件库 vant-ui 找到vant官网&#xff0c;进入va…

金融分析-Transformer模型(基础理论)

Transformer模型 1.基本原理 transformer的core是注意力机制&#xff0c;其本质就是编码器-解码器。他可以通过多个编码器进行编码&#xff0c;再把编码完的结果输出给解码器进行解码&#xff0c;然后得到最终的output。 1.1编码器 数据在编码器中会经过一个self-attention的…

【密码学】AES算法

一、AES算法介绍&#xff1a; AES&#xff08;Advanced Encryption Standard&#xff09;算法是一种广泛使用的对称密钥加密&#xff0c;由美国国家标准与技术研究院&#xff08;NIST&#xff09;于2001年发布。 AES是一种分组密码&#xff0c;支持128位、192位和256位三种不同…

朗致面试---IOS/安卓/Java/架构师

朗致面试---IOS/安卓/Java/架构师 一、面试概况二、总结三、算法题目参考答案 一、面试概况 一共三轮面试&#xff1a; 第一轮是逻辑行测&#xff0c;25道题目&#xff0c;类似于公务员考试题目&#xff0c;要求90分钟内完成。第二轮是技术面试&#xff0c;主要是做一些数据结…

51c嵌入式~单片机~合集2

我自己的原文哦~ https://blog.51cto.com/whaosoft/12362395 一、不同的电平信号的MCU怎么通信&#xff1f; 下面这个“电平转换”电路&#xff0c;理解后令人心情愉快。电路设计其实也可以很有趣。 先说一说这个电路的用途&#xff1a;当两个MCU在不同的工作电压下工作&a…

网络原理done

文章目录 ARP协议模拟一次ARP过程ARP周边问题ARP欺骗RARP DNS域名解析服务域名简介DNS结论 ICMP协议 NAT技术&#xff08;重点&#xff09;NAPTNAT缺点 内网穿透代理服务器正向代理反向代理 NAT和代理服务器区别 ARP协议 以这片区域为例 此时IP报文到达入口路由器R 此时路由器…

MATLAB中Simulink的信号线

Simulink以模块为最小单位,通过信号线互相连接&#xff0c;用户可通过GUI调配每个模块的参数,且仿真的结果能够以数值和图像等形象化方式具现出来。信号线可以传递一维数据、多维数据、向量数据或矩阵数据,甚至Bus型数据。Simulink使用不同的线形表示传递不同数据类型的信号线,…

集成方案 | Docusign + 泛微,实现全流程电子化签署!

本文将详细介绍 Docusign 与泛微的集成步骤及其效果&#xff0c;并通过实际应用场景来展示 Docusign 的强大集成能力&#xff0c;以证明 Docusign 集成功能的高效性和实用性。 在现代企业运营中&#xff0c;效率和合规性是至关重要的。泛微作为企业级办公自动化和流程管理的解决…

基于vue的quasarui框架和.NET CORE实现网站

首先安装quasar cli&#xff0c;然后进行配置 前台代码部分截图 后台部分截图 数据库 网站部分