zoukankan      html  css  js  c++  java
  • [笔记]ST表

    [笔记]ST表

    原题链

    算法用途

    ​ ST表主要用于解决RMQ问题(区间最值问题),可以做到(O(nlogn))预处理,(O(1))询问

    算法描述

    ​ ST表利用的是倍增的思想,以求区间最大值为例,我们用(Max[i][j]表示)从i位置开始的(2^j)个数中的最大值,例如(Max[i][1])表示的是(i)位置到第(2^1 - 1)个数,也就是第(i)个数的下一个数,这两个数的最大值.在转移的时候为我们考虑一个(max)操作的性质:(max(a,b,c) = max(max(a,b),max(b,c)))从这个性质我们发现,我们可以由两个较小的,用重叠的区间来推出一个大区间,因此我们可以少维护一些区间.

    ​ 知道上面的性质后,转移的式子就很好理解了.首先初始化的时候(f[i][0] = a[i])这表示从第(i)个数往后(2^0 - 1 = 0)个数也就是(i)这个数本身的值,所以直接赋值就好.接着对于包含几个数的区间的转移方程是(f[i][j]=max(f[i][j-1],f[i + 2^{j-1}][j-1]))这个式子是由上面的性质推出来的,首先我们假设(i = 1)这样方便计算与描述,那么根据上面的性质,我们可以将从第(1)个元素到第(2^j - 1)个元素的区间划分成从第(1)个元素到第(2^{j-1}个)元素的区间以及从第(2^{j-1} + 1)个元素到第(j)个元素这两个区间分更新最大值,这样就可以从小的区间推出大的区间,这也是符合上面讲的性质的.

    再来说查询,查询的时候会遇到很多问题,如果我们只从左端点去找,那么如果是要查询((1,7))的最小值怎么办呢?如果从(1)往后走(2^2),就只能查询((1,3)),但如果往后走(2^3),又查询的是((1,8)),所以并不好做.为了解决这个问题,我们可以直接从查询区间的两端一起找,现将区间的长度写成(2^k),接着从两端(l,r)分别找,找的区间重叠了也没有关系,用代码写这一段就是:

    int ask(int x,int y){//查询
    	int k = log2(y - x + 1 );
    	return max(maxx[x][k],maxx[y - (1 << k) + 1][k]);
    }
    

    但有一点要注意,在从右端点查询的时候为什么要(+1)呢?可以通过举例子来理解,比方说我们现在要查询的区间是((1,8)),那么(k=3),从左端点查就是查((1,7)),从右端点查如果不(+1)的话,就是查((0,6)),所以是要(+1)的.再来严谨的讲一下,实际上,从右端点查询就是要找到一个点满足(x + 2^k - 1 = r),移项之后就是(x = r - 2^k + 1).

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int maxx[1000010][50];
    int ask(int x,int y){//查询
    	int k = log2(y - x + 1 );
    	return max(maxx[x][k],maxx[y - (1 << k) + 1][k]);
    }
    int main(){
    	int n,m;
    	scanf("%d%d",&n,&m);
    	for(int i = 1;i <= n;i++){
    		scanf("%d",&maxx[i][0]);
    	}
    	for(int i = 1;i <= 21;i++){//预处理
    		for(int j = 1;j + (1 << i) - 1 <= n;j++){
    			maxx[j][i] = max(maxx[j][i - 1],maxx[j + (1 << (i - 1))][i - 1]);
    		}
    	}
    	for(int i = 1;i <= m;i++){
    		int l,r;
    		scanf("%d%d",&l,&r);
    		printf("%d
    ",ask(l,r));
    	}
    	return 0;
    }
    

    另:

    要注意在洛谷提交的时候一定要用(scanf),不能用(cin)否则会超时,同时要注意位运算的优先级,所以在预处理的时候那个括号很有讲究的(位运算的优先级是低于普通加减法的)

  • 相关阅读:
    Vue3使用vue3-video-player
    centos搭建phantomjs
    windows与Linux写入后门
    webService静态调用方法
    Web开发学习笔记(日更)
    docker修改容器绑定端口
    linux下~/.bashrc、/etc/profile、 $PATH环境变量 作用和修改
    docker for windows安装,修改images位置,修改镜像源,查看/var/lib/docker/containers
    hive修改表DDL
    python 高性能异步爬虫线程&线程池
  • 原文地址:https://www.cnblogs.com/czy--blog/p/13904010.html
Copyright © 2011-2022 走看看