Uva 11235 频繁出现的数值(RMQ-Sparse Table 算法)(训练指南)

来源:互联网 发布:监控端口号 编辑:程序博客网 时间:2024/06/11 11:02

题目链接: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;}


原创粉丝点击