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));

浙公网安备 33010602011771号