输入输出样例
输入 #1
5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5
输出 #1
4
4
1
4
4
分析
ST表:
我们设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示以i为左端点,长度为2^j的区间的最小值(最大值等)。
以最小值为例,显然可以有递推式
f
[
i
]
[
j
]
=
a
[
i
]
(
j
=
0
)
f[i][j]=a[i] (j=0)
f[i][j]=a[i](j=0)
f
[
i
]
[
j
]
=
m
(
f
[
i
]
[
j
−
1
]
,
f
[
i
+
2
(
j
−
1
)
]
[
j
−
1
]
)
(
j
>
0
)
f[i][j]=m(f[i][j-1],f[i+2^(j-1)][j-1]) (j>0)
f[i][j]=m(f[i][j−1],f[i+2(j−1)][j−1])(j>0)
建出来的f数组即为一个ST表。
中间的操作用位运算实现。
RMQ问题:
查询某个数组区间最小值或区间最大值。
因为最大值无法像前缀和那样可以相减,所以考虑使用ST表来预处理。
对于区间[l,r],其长度设为len,一定存在一个2k,满足len/2<=2k<len。此时[l,r]的最小值即为
m
i
n
(
f
[
l
]
[
k
]
,
f
[
r
−
2
k
+
1
]
[
k
]
)
min(f[l][k],f[r-2^k+1][k])
min(f[l][k],f[r−2k+1][k])。
按模板打就对了。
另外,莫队永远滴神!
上代码
洛谷还很贴心地给了快读的程序我就用了用。。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
typedef long long ll;
using namespace std;
int n,m,l,r,f[1000001][30];
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();
for(int i=1;i<=n;i++)
{
f[i][0]=read();
}
for(int j=1;j<=log2(n);j++)//数据预处理到2^20足够
{
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]);
}
}
for(int i=1;i<=m;i++)
{
l=read();r=read();
int k=log2(r-l+1);//区间长度
printf("%d\n",max(f[l][k],f[r-(1<<k)+1][k]));//printf永远滴神
}
return 0;
}