http://poj.org/problem?id=3264 (题目链接)
题意
给出序列,求区间最大值-最小值
Solution
无修改,询问较多,ST表水一发。
ST算法(Sparse Table):
它是一种动态规划的方法。以最小值为例。a为所寻找的数组,用一个二维数组 f(i,j) 记录区间 [i,i+2^j-1] 区间中的最小值。其中 f[i,0] = a[i] ; 所以,对于任意的一组 (i,j),f(i,j) = min{ f(i,j-1),f(i+2^(j-1),j-1)} 来使用动态规划计算出来。
这个算法的高明之处不是在于这个动态规划的建立,而是它的查询:它的查询效率是O(1)!如果不细想的话,怎么弄也是不会想到有O(1)的算法的。
假设我们要求区间[m,n]中a的最小值,找到一个数k使得2^k<n-m+1,即k=[ln(b-a+1)/ln(2)] 这样,可以把这个区间分成两个部分:[m,m+2^k-1]和[n-2^k+1,n]!我们发现,这两个区间是已经初始化好的!前面的区间是f(m,k),后面的区间是f(n-2^k+1,k)!这样,只要看这两个区间的最小值,就可以知道整个区间的最小值!
代码
// poj3264 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define inf 2147483640 #define Pi acos(-1.0) #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout); using namespace std; const int maxn=50010; int bin[30],a[maxn],mn[maxn][30],mx[maxn][30]; int n,m; void build() { for (int i=1;i<=n;i++) mx[i][0]=mn[i][0]=a[i]; for (int j=1;j<=20;j++) for (int i=1;i+bin[j]<=n+1;i++) mn[i][j]=min(mn[i][j-1],mn[i+bin[j-1]][j-1]); for (int j=1;j<=20;j++) for (int i=1;i+bin[j]<=n+1;i++) mx[i][j]=max(mx[i][j-1],mx[i+bin[j-1]][j-1]); } int query(int l,int r) { int x=log(r-l+1)/log(2); int a=max(mx[l][x],mx[r-bin[x]+1][x]); int b=min(mn[l][x],mn[r-bin[x]+1][x]); return a-b; } int main() { bin[0]=1;for (int i=1;i<=20;i++) bin[i]=bin[i-1]<<1; scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%d",&a[i]); build(); for (int x,y,i=1;i<=m;i++) { scanf("%d%d",&x,&y); printf("%d ",query(x,y)); } return 0; }