ST表专题总结
ST表(sparse table):用于静态RMQ(Range Minimum/Maximun Query)问题,不适用于带有修改操作的RMQ问题(要用线段树)。
ST表的建表复杂度为 O( nlogn ), 区间询问复杂度为O(1)。
学习网址 http://www.360doc.com/content/19/0406/15/5315_826796736.shtml
ST表常规模板:
#include <bits/stdc++.h>
#define ll long long
#define mem( f, x ) memset( f, x, sizeof( f ))
#define INF 0x3f3f3f3f
#define pii pair<int, int>
#define mk(x, y) make_pair(x, y)
#define fi first
#define se second
using namespace std;
const int N = 1e4 + 5;
const int M = 21;
int m, n;
int lg[N];
int f[N][M];
int Max( int x, int y ){
return x>y ? x:y;
}
void st( ){
for( int i = 1; i <= 21; i++ ){
for( int j = 1; j + ( 1<<i ) -1 <= n; j++ )
f[j][i] = Max( f[j][i-1], f[j+(1<<(i-1))][i-1] );
}
}
int query( int l, int r ){
int k = lg[r-l+1];
return Max( f[l][k], f[r-(1<<k)+1][k] ); //f[r-(1<<k)+1][k]:已知右端点r和区间长度1<<k,则左端点为r-(1<<k)+1
}
int main( ){
while( scanf( "%d %d", &n, &m ) != EOF ){
lg[0] = -1;
for( int i = 1; i <= n; i++ ){
lg[i] = lg[i/2] + 1; //底数为2的logn取整技巧
scanf( "%d", *f[i] );
}
while( m-- ){
int l, r;
scanf( "%d %d", &l, &r );
printf( "%d\n", query( l, r ) );
}
}
return 0;
}
例题讲解:
NO.1
A Magic Lamp HDU - 3183
题意: 给你一串长度为n的数字串,要求删掉m个字符数后剩下的字符所所形成的数字最小,其中 m <= n <= 1000。
错解: 去掉最大的m个字符。
反例: 17082 m = 1
很明显去掉7所得到的1082比去掉8所得到的1702要小
解法一:
贪心解法:假设删去s[i],则s[i]要满足从前往后遍历第一个满足s[i] > s[i+1],
暴力遍历,复杂度O(n^2)完全可以接受。
解法二:
考虑一位一位取n-m位数,第一位数肯定在s[1…m+1]中选取,因为若是从下标大于m+1的字符中选取则后面剩余的字符必然小于n-m-1,最终不足以凑成n-m个字符,选取的标准为s[1…m+1]中最小的字符。同样选取第二个字符时的范围为s[i+1…m+2],其中i为上一次(这里指第一次)选取的字符下标,至于