首页 > 其他分享 >Sparse Table

Sparse Table

时间:2024-10-18 19:58:48浏览次数:1  
标签:le log int 查询 Sparse 区间 Table 预处理

Sparse Table 可用于解决这样的问题:给出一个 \(n\) 个元素的数组 \(a_1, a_2, \cdots, a_n\),支持查询操作计算区间 \([l,r]\) 的最小值(或最大值)。这种问题被称为区间最值查询问题(Range Minimum/Maximum Query,简称 RMQ 问题)。预处理的时间复杂度为 \(O(n \log n)\),预处理后数组 \(a\) 的值不可以修改,一次查询操作的时间复杂度为 \(O(1)\)。

例题:P2880 [USACO07JAN] Balanced Lineup G

有一个包含 \(n\) 个数的序列 \(h_i\),有 \(q\) 次询问,每次询问 \(h_{a_i}, h_{a_i + 1}, \cdots, h_{b_i - 1}, h_{b_i}\) 中最大值与最小值的差。
数据范围:\(1 \le n \le 5 \times 10^4, \ 1 \le q \le 1.8 \times 10^5, \ 1 \le h_i \le 10^6, \ a_i \le b_i\)。

分析:题目要求最大值和最小值的差难以直接求出,通常需要分别求解最大值和最小值。最直接的做法是每次遍历区间中的每一个数,记录最大值和最小值。这样可以正确求出正确答案,但是效率低下,时间复杂度高达 \(O(nq)\),无法通过本题。

之所以这样做效率低下,是因为所有询问区间可能有着大量的重叠,这些重叠部分被多次遍历到,因此产生了大量的重复。如果可以通过预处理得到一些区间的最小值,再通过这些区间拼凑每一个询问区间,就可以提高效率。

预处理前缀和可以拼凑出任意区间的和,但是这个思路不能直接搬到最值查询问题中。原因在于区间和可以从一个大区间中减去一部分小区间得到,而区间最值不行,所以只能用小区间去拼出大区间。如何选择预处理的区间就成为关键,选择的区间既要能够拼出任意区间,数量少又不能太多,并且预处理和查询都要高效。

可以预处理以每一个位置为开头,长度为 \(2^0, 2^1, \cdots, 2^{\lfloor \log_2 n \rfloor}\) 的所有区间最值。下面以最大值为例,用 \(f_{i,j}\) 表示 \(h_i, h_{i+1}, \cdots, h_{i+2^j-2}, h_{i+2^j-1}\) 中的最大值,用递推的方式计算所有的 \(f\),转移为 \(f_{i,j} = \max (f_{i,j-1}, f_{i+2^{j-1}, j-1})\)。计算所有的 \(f\) 的过程为预处理,预处理的时间复杂度为 \(O(n \log n)\)。

image

void init() {
	for (int i = 1; i <= n; i++) {
		f[i][0] = h[i];
	}
	for (int j = 1; (1 << j) <= n; j++) {
		for (int i = 1; i <= n - (1 << j) + 1; i++) {
			f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
		} 
	}
}

接下来解决查询的问题,设需要查询最大值的区间是 \([l,r]\)。记区间长度为 \(L\),则该区间可以拆分为 \(O(\log L)\) 个小区间。对 \(L\) 做二进制拆分,从 \(l\) 开始向后跳,每次跳跃的量是一个 \(2\) 的幂,从而拼出整个区间。单词查询时间复杂度为 \(O(\log n)\)。

int query(int l, int r) {
	int len = r - l + 1, ans = -INF, cur = l;
	for (int i = 0; (1 << i) <= len; i++) {
		if ((len >> i) & 1) {
			ans = max(ans, f[cur][i]);
			cur += (1 << i);
		} 
	}
	return ans;
}

更进一步,查询区间最值时,区间合并的过程允许重叠,因此只需要找到两个长度为 \(2^k\) 的区间合并得到 \([l,r]\)。令 \(k\) 为满足 \(2^k \le r-l+1\) 的最大整数,区间 \([l, l+2^k-1]\) 和区间 \([r-2^k+1,r]\) 合并起来覆盖了需要查询的区间 \([l,r]\)。

image

int query(int l, int r) {
	int k = log_2[r - l + 1]; // 可以预处理log_2的表
	return max(f[l][k], f[r - (1 << k) + 1][k]);
}
参考代码
#include <cstdio>
#include <algorithm>
using std::min;
using std::max;
const int N = 50005;
const int LOG = 16;
int h[N], f_min[N][LOG], f_max[N][LOG], log_2[N];
void init(int n) {
    log_2[1] = 0;
    for (int i = 2; i <= n; i++) log_2[i] = log_2[i >> 1] + 1; // 预处理对数表
    for (int i = 1; i <= n; i++) {
        f_min[i][0] = f_max[i][0] = h[i];
    }
    for (int j = 1; (1 << j) <= n; j++) {
        for (int i = 1; i <= n - (1 << j) + 1; i++) {
            f_min[i][j] = min(f_min[i][j - 1], f_min[i + (1 << (j - 1))][j - 1]);
            f_max[i][j] = max(f_max[i][j - 1], f_max[i + (1 << (j - 1))][j - 1]);
        }
    }
}
int query(int l, int r, int flag) { // flag为1时查询最大值,为0时查询最小值
    int k = log_2[r - l + 1];
    if (flag) return max(f_max[l][k], f_max[r - (1 << k) + 1][k]);
    else return min(f_min[l][k], f_min[r - (1 << k) + 1][k]);
}
int main()
{
    int n, q; scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i++) scanf("%d", &h[i]);
    init(n);
    for (int i = 1; i <= q; i++) {
        int a, b; scanf("%d%d", &a, &b);
        printf("%d\n", query(a, b, 1) - query(a, b, 0));
    }
    return 0;
}

Sparse Table 预处理部分的时间复杂度为 \(O(n \log n)\),查询一次的时间复杂度为 \(O(1)\),总的时间复杂度为 \(O(n \log n)\)。

Sparse Table 不仅可以求区间最大值和最小值,还可以处理符合结合律和幂等律(与自身做运算,结果仍是自身)的信息查询,如区间最大公约数、区间最小公倍数、区间按位或、区间按位与等。

标签:le,log,int,查询,Sparse,区间,Table,预处理
From: https://www.cnblogs.com/ronchen/p/18474958

相关文章

  • 【AI绘画】Stable Diffusion实战ControlNET插件(让小姐姐摆出你要的pose!)
    大家好我是安琪!SD插件ControlNET的诞生,无法自定义姿势成为过去,自定义姿势;根据线稿、骨骼、其他图片生成全新的图,AI绘图自主可控;包括边缘检测,深度信息估算;姿态,手势检测;分割等等场景:个人pose图,模特换装;装修出图;设计草图快速复原;颜色快速更换等等此扩展用于AUTOMATIC1111的......
  • 五款免费报表工具推荐:山海鲸报表、Tableau 等优劣对比
    在当今数据驱动的时代,报表工具已经成为各类企业进行决策和管理的重要工具。无论是大中型企业还是小微企业,能够快速、高效地生成可视化报表,洞察业务运营情况,已经成为提升竞争力的关键。今天为大家挑选了5款非常优秀的报表软件,并且详细分析了它们的优缺点,希望能够帮助你选出最适合的......
  • td设置padding:0后el-table展示不全
    <el-table   ref="table"   class="city-report-table-container"   border   height="100%"   :data="tableData"   :span-method="tableSpanMethod"  ><el-table-columnlabel=&q......
  • 还有小白不会用stable diffusion?史上最全的stable diffusion环境配置指南
    前言StableDiffusion的横空出世,带动了AI生成图片的又一波高潮。随后在StableDiffusion的模型基础上,各种风格、生成内容的再训练模型层出不穷,极大的丰富了AI生成图片的多样性和精细程度;Lora、ControlNet等插件的出现,更加简化了模型的训练难度以及优化了图片生成的预期效果......
  • table
    Luatable(表)table是Lua的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。Luatable使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是nil。Luatable是不固定大小的,你可以根据自己需要进行扩容。Lua也是通过table来解决模块(module)、......
  • 解决TypeError: 'NoneType' object is not subscriptable
    1.捕获异常的方式try:img_list=img_list["name"]except:img_list=""2.对象进行判断ifimg_list:img_list=img_list["name"]else:img_list=""demotextJson=json.loads(res.text)#转json对象iftextJson:##整个对象都......
  • vue,xlsx,xlsx-style,file-saver,生成Excel并导出,cptable报错,合并单元格 样式缺失
    一,安装依赖 二,导入依赖import*asXLSXfrom'xlsx';import*asXLSX_STYLEfrom'xlsx-style'import{saveAs}from'file-saver';三,解决引入xlsx-style./cptable模块找不到问题Thisrelativemodulewasnotfound:*./cptablein./node_modules......
  • vue3.0 使用Element Plus修改el-table表格的横纵滚动条颜色、宽高等样式
    在Vue3.0和ElementPlus中修改el-table的滚动条样式,可能遇到了样式不生效的问题。这通常是因为ElementPlus使用了自定义的滚动条组件el-scrollbar,而不是浏览器默认的滚动条。因此,需要针对el-scrollbar组件进行样式定制。<stylescoped>/*滚动条整体部分*/......
  • 文生图:Stable Diffusion、Midjourny
    StableDiffusion(SD)和Midjourney(MJ)是当前流行的两款AI图像生成工具,它们各有特点和优势:**-StableDiffusion是完全开源的,**这意味着用户可以免费使用,并且有技术能力的用户可以自行修改和优化模型。很多国内的公司,都是基于这个模型,本地部署,自己只开发前端应用。StableDiff......
  • 【奶奶看了都会了】AI绘画 Mac安装stable-diffusion-webui绘制AI妹子保姆级教程
    1.作品图2.准备工作目前网上能搜到的stable-diffusion-webui的安装教程都是Window和MacM1芯片的,而对于因特尔芯片的文章少之又少,这就导致我们还在用老Intel芯片的Mac本,看着别人生成美女图片只能眼馋。所以这周末折腾了一天,总算是让老Mac本发挥作用了。先来说说准备工作:......