题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2176
范围最小值问题(Range Minimum Query,RMQ).实践中最常用的是Sparse Table 算法,预处理时间是O(nlogn),查询只需要O(1),而且常数很小。
注意到整个数组是非降序的,所有相等元素会聚集到一起。这样可以把整个数组进行游程编码。比如-1,1,1,2,,2,2,4可以编码成(-1,1)(1,2),(2,3),(4,1),其中(a,b)表示有b个连续的a.
#include <cstdio> #include <vector> #include <algorithm> #include <iostream> using namespace std; const int maxn=100000+5; int a[maxn],l[maxn],r[maxn],num[maxn]; ///num[p]表示位置p所在段的编号 ///l[p],r[p]分别表示所在段左右端点的位置 int d[maxn][25]; //vector<int> count; void RMQ_init(const vector<int>& A) { int v=A.size(); for(int i=1;i<v;i++) d[i][0]=A[i]; for(int j=1;(1<<j)<=v;j++) for(int i=0;i+(1<<j)-1<v;i++) d[i][j]=max(d[i][j-1],d[i+(1<<(j-1))][j-1]); } int RMQ(int L,int R) { int k=0; while((1<<(k+1))<=R-L+1) k++;///如果2^(k+1)<=R-L+1,那么k还可以加1 return max(d[L][k],d[R-(1<<k)+1][k]); } int main() { int q,n; while(~scanf("%d",&n)&&n!=0){ scanf("%d",&q); for(int i=0;i<n;i++) scanf("%d",&a[i]); a[n]=a[n-1]+1; int start=-1; vector<int> count; for(int i=0;i<=n;i++) if(i==0||a[i]>a[i-1]){ if(i>0){ count.push_back(i-start); for(int j=start;j<i;j++){ num[j]=count.size()-1,l[j]=start,r[j]=i-1; } } start=i; } RMQ_init(count); while(q--){ int L,R,ans; scanf("%d%d",&L,&R); L--;R--; if(num[L]==num[R]) ans=R-L+1; else{ ///最后结果是从L到L所在段结束的元素个数, ///从R所在段开始处到R处元素个数, ///中间第num[L]+1段到第num[R]-1段的count的最大值 ans=max(r[L]-L+1,R-l[R]+1); if(num[L]+1<=num[R]-1) ans=max(ans,RMQ(num[L]+1,num[R]-1)); } printf("%d\n",ans); } } return 0; }