ST表例题

这是一些可以用ST表解决的例题啊

题目链接尽量不会用本校 OJ 的链接(有些是懒得找)

ST 表的核心 dp 一定不要打错。。。尤其注意一下位运算的优先级,该打括号就老老实实打。。

for(int j=1;j<=Log[n];j++){
        for(int i=1;i+(1<<j)-1<=n;i++){
            fmx[i][j]=max(fmx[i][j-1],fmx[i+(1<<j-1)][j-1]);
            fmn[i][j]=min(fmn[i][j-1],fmn[i+(1<<j-1)][j-1]);
        }
    }

(应该都看得懂)

1.区间最大值

题意:输入一串数字,给你 \(M\) 个询问,每次询问就给你两个数字 \(X,Y\) ,要求你说出 \(X\)\(Y\) 这段区间内的最大数。

思路:ST表模板。

链接

2.「USACO2007JAN」Balanced Lineup

题意:求区间 \([l,r]\)\(max-min\)

思路:注意这一句话:

牛总是按同一序列排队

这意味着它是一个静态问题,才可以用ST表

链接

3.与众不同

题意:给你一个长度为 \(N\)的序列,定义“完美序列”是一段区间满足区间内使用的数互不相同 询问 \(M\) 次,问区间\([L,R]\) 之间最长完美序列的长度

思路:

问题简化:如何求解出所有的最长完美序列?

\(last[s]\) 表示值为 \(s\) 最后一次出现的位置;\(st[i]\) 表示以 \(a_i\) 结尾的最长完美序列起点;\(f[i]\) 表示以 \(a_i\) 结尾的最长完美序列长度。

遍历1到 \(n\)\(st[i]=\max(st[i-1],last[a[i]]+1)\) \(\to\) \(st\) 数组单调不下降

\(f[i]=i-st[i]+1\) ,更新 \(last[a[i]]\)\(O(n)\) 求解。

询问区间 \([X,Y]\) ?

对于区间中的 \(a_i\) 结尾的最长完美序列的起点\(st[i]\)可能出现在\(X\)左边,也可能出现在\([X,Y]\)上。
假设下标为 \(X-k\)\(st\) 数组在 \(X\) 左边,\(k+1-Y\)\(st\) 数组在区间 \([X,Y]\) 上。
因为 \(st\) 数组单调不降,可以二分查找 \(k\) 值。
那么区间 \([X,Y]\) 的最长完美序列长度可以分为两部分:

  • \(st\) 数组在X左边的部分,最长长度为 \(k-X+1\)

  • \(st\) 数组在 \([X,Y]\) 上,那么在区间 \([K+1,Y]\) 找最大值 \(f[i]\)(ST算法)

时间复杂度为 \(((M+N)logN)\)

链接

4.数字对

题意: 小H是个善于思考的学生,现在她又在思考一个有关序列的问题。 她的面前浮现出一个长度为 \(n\) 的序列 \(a_i\),她想找出一段区间 \([L, R](1 <= L <= R <= n)\)。这个特殊区间满足,存在一个 \(k(L <= k <= R)\),并且对于任意的 \(i(L <= i <= R)\)\(a_i\) 都能被 \(a_k\) 整除。这样的一个特殊区间 \([L, R]\) 价值为 \(R - L\)
小H想知道序列中所有特殊区间的最大价值是多少,而有多少个这样的区间呢?这些区间又分别是哪些呢?你能帮助她吧。

思路:特殊区间满足的要求可以转化为:\(\min(a_i)==gcd(a_l,a_{l+1},...,a_r),i\in(l,r)\)

于是就转化成了一个 RMQ 问题,注意 GCD 也可以用 ST 表解决

好的,现在我们知道如何 \(O(1)\) 判断一个区间是否是特殊区间,如何求答案?

暴力怎么暴力?枚举最大价值,再 \(O(n)\) 扫一遍?这样是 \(n^2\) 的复杂度,爆了

于是我们可以二分答案最大价值

链接

5.「HAOI2007」理想的正方形

题意:有一个 \(a*b\) 的整数组成的矩阵,现请你从中找出一个 \(n*n\) 的正方形区域,使得该区域所有数中的最大值和最小值的差最小。

思路:庆幸查询是正方形,不然完全可以查询一个极窄的长方形,这样 ST 表就很难受了。

其实就是二维 ST 嘛:

for(int k=1;(1<<k)<=a;k++){
        for(int i=1;i+(1<<k)-1<=a;i++){
            for(int j=1;j+(1<<k)-1<=b;j++){
                mx[i][j][k]=max(mx[i][j+(1<<k-1)][k-1],max(mx[i+(1<<k-1)][j][k-1],
                            max(mx[i][j][k-1],mx[i+(1<<k-1)][j+(1<<k-1)][k-1])));
                mn[i][j][k]=min(mn[i][j+(1<<k-1)][k-1],min(mn[i+(1<<k-1)][j][k-1],
                            min(mn[i][j][k-1],mn[i+(1<<k-1)][j+(1<<k-1)][k-1])));
            }
        }
    }
 int ans=2e9,k=log(n)/log(2);
    for(int i=1;i+n-1<=a;i++)
		for(int j=1;j+n-1<=b;j++){
			int maxx=max(mx[i][j][k],max(mx[i+n-(1<<k)][j][k]
            ,max(mx[i][j+n-(1<<k)][k],mx[i+n-(1<<k)][j+n-(1<<k)][k])));
			int	minn=min(mn[i][j][k],min(mn[i+n-(1<<k)][j][k]
            ,min(mn[i][j+n-(1<<k)][k],mn[i+n-(1<<k)][j+n-(1<<k)][k])));
			ans=min(ans,maxx-minn);
		} 

(良心作者的良心代码缩进被狗啃了)

还有一种解法是:维护单调队列,\(maxx[i][j]\)表示第\(i\)行以\(j\)为结尾的长度为\(min(j,len)\)的序列最大值;\(minn[i][j]\)表示第\(i\)行以\(j\)为结尾的长度为\(min(j,len)\)的序列最小值.
可以相当于使之缩成一个点,因此有\(ansx[i][j]\)表示以\((i,j)\)为右下方端点,边长为\(min(j,len)\)的正方形的最大值;\(ansn[i][j]\)表示以\((i,j)\)为右下方端点,边长为\(min(j,len)\)的正方形的最小值.
最后 \(n^2\) 枚举右下方端点更新答案即可.

链接

6.「NOI2010」超级钢琴

题意:有 \(n\) 个音符,编号为 1 至 \(n\) 。第 \(i\) 个音符的美妙度为 \(A_i\)

我们要找到 \(k\) 段超级和弦组成的乐曲,每段连续的音符的个数 \(x\) 满足 \(L\leq x\leq R\) ,求乐曲美妙度的最大值。

思路:贪心+堆+RMQ

最朴素的想法是,枚举端点 \(l,r\quad(L<=r-l<=R)\)

然后挨个计算显然太蠢了,可以前缀和小小优化一下,然后把得到的“美妙度”插入一个优先队列(堆),贪心地取 前 \(k\) 个显然就可以了。时间复杂度是 \(O(n^2logn)\)

显然T掉,但可以给我们一个启发:对于同一个端点 \(l\) ,在有限的对应端点 \(r\) 中,只有最大的那几个才有可能贡献入答案。

不妨设 \(Max(o,l,r)=\max\{sum[t]-sum[o-1]|1\leq t\leq r\}\)

即以 \(o\) 为左端点,右端点范围是 \([l, r]\) 的最大子段。\(sum\) 数组也就是前缀和。

这也就转化成了一个RMQ问题,因为 \(sum[o-1]\) 显然是固定的,也就是求 \(sum[t]_{max}\)

注意到有可能同一个 \(o\) 可能会多次贡献答案,所以在取对顶的时候要:

if (l != t) Q.push(node(o, l, t - 1)); 
if (t != r) Q.push(node(o, t + 1, r));

链接

posted @ 2021-02-21 00:10  _Famiglistimo  阅读(300)  评论(0)    收藏  举报