• 学习记录:ST表


    ST表简介

    ST 表是用于解决 可重复贡献问题 的数据结构

    可重复贡献问题:是指对于运算(opt),满足(x opt x =x),则对应的区间询问就是一个可重复贡献问题。(摘自OI wiki)

    说人话,就是对区间的重复运算不影响结果。比如区间最值问题(RMQ),区间GCD

    比方10个数求最大值,先对前7个数求最值,再对后6个数求最值,中间有一段重复没有关系,最后再对求出的两个最值再求一次最值,这种方法是正确的。
    按照第一段的话说,就是

    [max(1,2,...,10)=max( max(1,2,...,7) , max(4,5,...,10) ) ]

    ST表主要运用了倍增的思想,可以做到(O(nlog_2n))预处理,(O(1))回答每次询问,但不支持修改操作

    具体实现

    预处理

    首先,令(f(i,j))表示区间([i,i+2^j-1])的最值

    显然,(f(i,0))表示区间([i,i+2^0-1]),也就是(a_i)的值

    在其他维度中,(f(i,j))表示区间([i,i+2^j-1])的最值,相比起(f(i,j-1))而言,所表示的区间范围扩大了两倍。最开始提到过,对一个区间求最值的问题可以转换为对两个小区间分别求最值,既然是两倍范围,那么就可以拆成两个区间,这样就可以通过上一维度的信息求出当前维度的信息。

    N5UWss.jpg

    (f(i,j)=max(f(i,j-1),f(i+2^{j-1},j-1)))

    那么预处理的代码不难写出

    void pre()
    {
    	for (int i=1;i<=n;i++)	//第一维的数据输入 
    		cin>>f[i][0];
    		
    	for (int j=1;j<=21;j++)	//2^21大概是
    		for (int i=1;i+(1<<j)-1<=n;i++)
    			f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);//"<<"是左移运算,1<<x就是2^x
    }
    

    计算

    预处理中,每一层维度的覆盖范围都是(2^x),当然 每次询问的范围 都不可能刚好是这样的指数范围,因此,还要对运算的区间进行处理

    根据可重复贡献问题的定义,重复区间的计算并不会对最值产生影响,那么就可以将待求区间划分为两个子区间

    我们希望(l+2^x-1=r),解得维度(x)应当为(log_2(r-l+1)),显然(x)不一定是个整数,因此要进行取整操作
    向上取整显然不行,因为引入了新区间
    因此只能向下取整,当然覆盖范围会变小。解决方法便是再引入一个相同长度,但出发点不同的区间,他们的并集会覆盖整个所求区域。

    N5yOyT.jpg

    如图,第一个区间是向下取整,出发点依旧是(l)的区间,覆盖范围是([l,l+2^{log_2(r-l+1)}-1]),也就是(f(l,[x]))
    第二个区间向前移动了(r-2^{[x]}),即(f(r-2^{[x]}+1,[x]))

    最多只需要两个区间,便能求出任意区间上的最值,因此查询效率(O(1))

    代码如下:

    void read()
    {
        int l,r;
        cin>>l>>r;
        int lg=logn[r-l+1];	//logn是提前写好的向下取整的log数值
        int ans=max(f[l][lg],f[r-(1<<lg)+1][lg]);
        printf("%d
    ",ans);
    }
    

    最后附一道模板题,【模板】ST表

    #include <bits/stdc++.h>
    
    using namespace std;
    const int maxn = 1e5+10;
    const int mod  = 100003;
    
    int f[maxn][21];
    int logn[maxn],n,m;
    void pre()
    {
    	logn[1]=0,logn[2]=1;
    	for (int i=3;i<=n;i++)
    		logn[i]=logn[i/2]+1;
    }
    inline int read()
    {
    	int x=0,f=1;char ch=getchar();
    	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    	return x*f;
    }
    int main()
    {
    	n=read(),m=read();//先读取了n才知道预处理的范围!
    	pre();
    	for (int i=1;i<=n;i++)
    		f[i][0]=read();
    	for (int j=1;j<=21;j++)
    		for (int i=1;i+(1<<j)-1<=n;i++)
    			f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    	while (m--)
    	{
    		int l=read(),r=read();
    		int lg=logn[r-l+1];
    		int ans=max(f[l][lg],f[r-(1<<lg)+1][lg]);
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    什么是浮动IP
    How can I detect multiple logins into a Django web application from different locations?
    git add -A使用说明
    理解水平扩展和垂直扩展
    php != 和 !== 的区别
    wireshark:Couldn't run /usr/bin/dumpcap in child process: Permission denied
    Unable to VNC onto Centos server remotely
    systemctl使用说明
    KiB、MiB与KB、MB的区别
    python带setup.py的包的安装
  • 原文地址:https://www.cnblogs.com/Salty-Fish/p/13213919.html
Copyright © 2020-2023  润新知