ST表
思路
\(ST\) 表可以在 \(O(n\log n)\) 的预处理以后 \(O(1)\) 的解决 \(RMQ\) 问题,并且不能带修。
\(RMQ\) 问题有这样一个特质:重复计算并不会影响最终结果,例如计算一个点最大值的两次并不会改变最终的区间最大值,因此 \(ST\) 表的作法也由此而生。首先 \(n\log n\) 预处理出每一个点后长度为 \(2^k,k\in \mathbb{N^+},2^k+i\leq n\) 的最大值,合并时只需合并最恰好大于等于区间一半长度的长度即可。
ST表
思路
版题。
\(Code\)
#include<bits/stdc++.h>
using namespace std;
int a[100005];
int lg[100005];
int maxn[100005][25];
int n, m, l, r;
template<typename T>
inline void read(T&x){
x = 0; char q; bool f = 1;
while(!isdigit(q = getchar())) if(q == '-') f = 0;
while(isdigit(q)) x = (x<<1) + (x<<3) + (q^48), q = getchar();
x = f?x:-x;
}
template<typename T>
inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x/10);
putchar(x%10^48);
}
int main(){
read(n), read(m);
lg[0] = -1;
for(register int i = 1; i <= n; ++i){
read(a[i]);
maxn[i][0] = a[i];
lg[i] = lg[i/2]+1;
}
for(register int i = 1; i <= lg[n]; ++i)
for(register int j = 1; j+(1<<i)-1 <= n; ++j)
maxn[j][i] = max(maxn[j][i-1], maxn[j+(1<<(i-1))][i-1]);
while(m--){
read(l), read(r);
int len = lg[r-l+1];
write(max(maxn[l][len], maxn[r-(1<<(len))+1][len]));
putchar('\n');
}
return 0;
}
Balanced Lineup G
思路
维护一个 \(maxn\) 和一个 \(minn\),\(st\) 表线段树都可以。
\(Code\)
#include<bits/stdc++.h>
using namespace std;
int a[100005];
int lg[100005];
int maxn[50005][25], minn[50005][25];
int n, m, l, r;
template<typename T>
inline void read(T&x){
x = 0; char q; bool f = 1;
while(!isdigit(q = getchar())) if(q == '-') f = 0;
while(isdigit(q)) x = (x<<1) + (x<<3) + (q^48), q = getchar();
x = f?x:-x;
}
template<typename T>
inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x/10);
putchar(x%10^48);
}
int main(){
read(n), read(m);
lg[0] = -1;
for(register int i = 1; i <= n; ++i){
read(a[i]);
maxn[i][0] = a[i];
minn[i][0] = a[i];
lg[i] = lg[i/2]+1;
}
for(register int i = 1; i <= lg[n]; ++i)
for(register int j = 1; j+(1<<i)-1 <= n; ++j)
maxn[j][i] = max(maxn[j][i-1], maxn[j+(1<<(i-1))][i-1]), minn[j][i] = min(minn[j][i-1], minn[j+(1<<(i-1))][i-1]);
while(m--){
read(l), read(r);
int len = lg[r-l+1];
write(max(maxn[l][len], maxn[r-(1<<(len))+1][len])-min(minn[l][len], minn[r-(1<<(len))+1][len]));
putchar('\n');
}
return 0;
}
3.[USACO07JAN] Balanced Lineup G
思路
版题但是 \(\min \max\) 同时处理版本。
\(Code\)
#include<bits/stdc++.h>
using namespace std;
int a[50005];
int lg[50005];
int maxn[50005][25], minn[50005][25];
int n, m, l, r;
template<typename T>
inline void read(T&x){
x = 0; char q; bool f = 1;
while(!isdigit(q = getchar())) if(q == '-') f = 0;
while(isdigit(q)) x = (x<<1) + (x<<3) + (q^48), q = getchar();
x = f?x:-x;
}
template<typename T>
inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x/10);
putchar(x%10^48);
}
int main(){
read(n), read(m);
lg[0] = -1;
for(register int i = 1; i <= n; ++i){
read(a[i]);
maxn[i][0] = a[i];
minn[i][0] = a[i];
lg[i] = lg[i/2]+1;
}
for(register int i = 1; i <= lg[n]; ++i)
for(register int j = 1; j+(1<<i)-1 <= n; ++j)
maxn[j][i] = max(maxn[j][i-1], maxn[j+(1<<(i-1))][i-1]), minn[j][i] = min(minn[j][i-1], minn[j+(1<<(i-1))][i-1]);
while(m--){
read(l), read(r);
int len = lg[r-l+1];
write(max(maxn[l][len], maxn[r-(1<<(len))+1][len])-min(minn[l][len], minn[r-(1<<(len))+1][len])), puts("");
}
return 0;
}
P7809 [JRKSJ R2] 01 序列
思路
对于问题一,这是可以记录一个 \(0\) 的前缀和,一个 \(1\) 的后缀和,可以得知对于点 \(i\),\(ans=pre_{i}-pre_{l-1}+suf_{i}-suf_{r+1}\)。该部分直接使用 \(ST\) 表即可实现。当然,这里会存在两种特殊情况,即全为 \(0\) 或者全为 \(1\),需特判即可。
对于问题二是比较好处理的,记录下每个位置最近的前面的 \(1\) 的与每个位置最近的后面的 \(0\),在区间中如果 \(pre0_l<pre1_r\),即说明存在一组 \(01\)。
\(Code\)
#include<bits/stdc++.h>
using namespace std;
int n, m;
bool a[1000005];
int op, l, r;
int pre[1000005], lg[1000005]; //pre为前缀和
int pre1[1000005], pre0[1000005]; //pre1为前方的第一个1位置,pre0为后方的第一个0位置
int maxn[2000005][25];
template<typename T>
inline void read(T&x){
x = 0; char q; bool f = 1;
while(!isdigit(q = getchar())) if(q == '-') f = 0;
while(isdigit(q)){
x = (x<<1) + (x<<3) + (q^48);
q = getchar();
}
x = f?x:-x;
}
template<typename T>
inline void write(T x){
if(x < 0){
putchar('-');
x = -x;
}
if(x > 9) write(x/10);
putchar(x%10^48);
}
int main(){
read(n), read(m);
lg[0] = -1;
for(register int i = 1; i <= n; ++i){
read(a[i]);
pre[i] = pre[i-1]+a[i];
lg[i] = lg[i>>1]+1;
if(a[i]) pre1[i] = i;
else pre1[i] = pre1[i-1];
maxn[i][0] = i-2*pre[i];
}
pre0[n+1] = n+1;
for(register int i = n; i >= 1; --i){
if(!a[i]) pre0[i] = i;
else pre0[i] = pre0[i+1];
}
for(register int i = 1; i <= lg[n]; ++i)
for(register int j = 1; j <= n; ++j)
maxn[j][i] = max(maxn[j][i-1], maxn[j+(1<<i-1)][i-1]);
for(register int i = 1; i <= m; ++i){
read(op), read(l), read(r);
if(op == 1){
write(max(max(pre[r]-pre[l-1], (r-l+1)-(pre[r]-pre[l-1])), max(maxn[l][lg[r-l+1]], maxn[r-(1<<lg[r-l+1])+1][lg[r-l+1]])-l+1+pre[r]+pre[l-1])), puts("");
}
if(op == 2){
if(pre0[l]<pre1[r]) puts("2");
else puts("1");
}
}
return 0;
}