本蒟蒻的blog:https://startcraft.cn
ST表概述
ST是解決區間RMQ(區間最值)問題的一種數據結構,它不支持在線修改,預處理\(O(nlogn)\),查詢\(O(1)\)
實現
以區間最小值爲例子
預處理
\(ans[i][j]\)表示區間\([i,i+2^{j}-1]\)的最小值,同時它可以表示成前半個區間的最小值和後半個區間的最小值
前半個區間是\([i,i+2{j-1}-1]\),可以表示成\(ans[i][j-1]\),那麼後半個區間是\([i+2{j-1},i+2{j}-1]\),可以表示成\(ans[i+2{j-1}][j-1]\)
即
所以初始化的時間複雜度是\(O(nlogn)\)
實現代碼
int en = log2(n);
wfor(i, 0, 31)
{
bit[i] = 1 << i;//表示2^i
}
for(i=1;i<n + 1;i++)//初始化區間長度爲1的
{
minnum[i][0] = num[i];
}
for(j=1;j<en + 2;j++)
{
for(i=1;i<n + 1;i++)
{
if (i + bit[j - 1] <= n)
{
minnum[i][j] = min(minnum[i][j - 1], minnum[i + bit[j - 1]][j - 1]);
}
}
}
查詢
假設我們要查詢的區間爲\([l,r]\),那麼區間長度爲\(len=r-l+1\)
設\(t=\log(len)\)
我們知道\(2^{\log(a)}>a/2\)
所以\(2^{t}>len\)
所以區間\([l,l+2{t}-1]\)大於等於所求區間的一半,同理從\(r\)往前數\(2{t}\)的區間也大於等於所求區間長度的一半,那麼我們只要求出這兩個區間最小值的最小值就是所求區間的最小值
這兩個區間我們前面已經預處理出來了,從\(r\)往前數\(2{t}\)是\(r-2{t}+1\).
所以所求區間的最小值爲\(min(ans[l][t]),ans[r-2^{t}+1][t]\)
實現代碼
int t = log2(len);
int ans = min(minnum[l][t], minnum[r - bit[t] + 1][t]);
例題 POJ-3264
AC代碼
#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
typedef long long ll;
#define wfor(i,j,k) for(i=j;i<k;++i)
#define mfor(i,j,k) for(i=j;i>=k;--i)
// void read(int &x) {
// char ch = getchar(); x = 0;
// for (; ch < '0' || ch > '9'; ch = getchar());
// for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
// }
int bit[32];
void init()
{
int i;
wfor(i, 0, 31)
{
bit[i] = 1 << i;
}
}
const int maxn = 5e4 + 5;
int num[maxn];
int maxnum[maxn][30];
int minnum[maxn][30];
int main()
{
int n, q;
// cin >> n >> q;
scanf("%d%d", &n, &q);
int i;
init();
wfor(i, 1, n + 1)
{
// cin >> num[i];
scanf("%d", &num[i]);
}
int j;
int en = log2(n);
wfor(i, 1, n + 1)
{
maxnum[i][0] = num[i];
minnum[i][0] = num[i];
}
wfor(j, 1, en + 2)
{
wfor(i, 1, n + 1)
{
if (i + bit[j - 1] <= n)
{
maxnum[i][j] = max(maxnum[i][j - 1], maxnum[i + bit[j - 1]][j - 1]);
minnum[i][j] = min(minnum[i][j - 1], minnum[i + bit[j - 1]][j - 1]);
}
}
}
wfor(i, 0, q)
{
int l, r;
// cin >> l >> r;
scanf("%d%d", &l, &r);
int len = r - l + 1;
int t = log2(len);
int x = max(maxnum[l][t], maxnum[r - bit[t] + 1][t]);
int y = min(minnum[l][t], minnum[r - bit[t] + 1][t]);
// cout << x - y << endl;
printf("%d\n", x - y);
}
return 0;
}