【RMQ|ST】POJ-3368 Frequent values

来源:互联网 发布:中教数据库论文 编辑:程序博客网 时间:2024/05/23 01:24
Frequent values
Time Limit: 2000MS Memory Limit: 65536K   

Description

You are given a sequence of n integers a1 , a2 , ... , an in non-decreasing order. In addition to that, you are given several queries consisting of indices i and j (1 ≤ i ≤ j ≤ n). For each query, determine the most frequent value among the integers ai , ... , aj.

Input

The input consists of several test cases. Each test case starts with a line containing two integers n and q (1 ≤ n, q ≤ 100000). The next line contains n integers a1 , ... , an (-100000 ≤ ai ≤ 100000, for each i ∈ {1, ..., n}) separated by spaces. You can assume that for each i ∈ {1, ..., n-1}: ai ≤ ai+1. The following q lines contain one query each, consisting of two integers i and j (1 ≤ i ≤ j ≤ n), which indicate the boundary indices for the 
query.

The last test case is followed by a line containing a single 0.

Output

For each query, print one line with one integer: The number of occurrences of the most frequent value within the given range.

Sample Input

10 3-1 -1 1 1 1 1 3 10 10 102 31 105 100

Sample Output

143

Source

Ulm Local 2007

————————————————————难过的分割线————————————————————

思路:看了一晚上大白书上的RMQ,一上来就给了这么个例题,简直无情。

首先要通过游程编码(RLE)进行建模。意即存每个数的出现个数。然后对这个数组进行查询,变成了RMQ问题。

思路是ST算法。ST——照我的理解来看,像是一种压缩,由于我先看了线段树,所以感觉ST是一种在log级别上的压缩。仅用二维数组、DP思想解决了区间最值问题。对于初始化——d[i][j]的[i]是从第 i 个数到第 n 个数,表示起点。[j]表示2^j个数的性质。意即连续1个数的性质、连续2个数的性质、4个数、8个数……

之后要怎么实现查询呢?设变量k,找到这样一个值,使得对于某区间、左起2^k个数和右起2^k个数可以有交集地覆盖此区间。只要有交集地覆盖了,那么最值的求解变得很简单了,就算中间有重叠,没关系。O(1)。

如果真的想理解,那么请仔细看大白书和这份注释。原始参考代码来自:too_weak

代码如下:

/****************************************/ #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #include <stack> #include <queue> #include <vector> #include <map> #include <string> #include <iostream> using namespace std;/****************************************/const int N = 100005;int a[N], l[N], r[N], num[N], d[N][25];void R_init(const vector<int> &A)//初始化{    int n = A.size();    for(int i = 0; i < n; i++)d[i][0] = A[i];//递推的起始条件    for(int j = 1; (1<<j) <= n;j++)        for(int i = 0; i + (1<<j) - 1 < n; i++)            d[i][j] = max(d[i][j-1], d[i+(1<<(j-1))][j-1]);}//初始化的时候d[][]保存2^k个数的最值,即从i向右2个数的最值、从i向右4个数的最值、从i向右8个数的最值……int R_quer(int LL, int RR)//查询的时候,用两个2^k去覆盖查询区间{    int k = 0;    while((1<<(k+1)) <= RR - LL + 1)k++;//注意,如果2^(k+1)大于查询区间数的个数,那么从LL向右2^k个数与从RR向左2^k个数一定有交集地覆盖查询区间    return max(d[LL][k], d[RR-(1<<k)+1][k]);}void R_rle(int n)//游程编码{int sta = 0;vector<int> A;for(int i = 1; i <= n; i++)if(a[i] > a[i-1]) {for(int j = sta; j < i; j++) {num[j] = A.size();//num[]保存每个数所在的段l[j] = sta;//l[]保存各段左端点r[j] = i - 1;//r[]保存各段右端点}A.push_back(i-sta);sta = i;//sta记录每个元素首次出现的位置}R_init(A);//初始化}int main(){    int n, op;    while(~scanf("%d", &n)) {if(!n)break;        scanf("%d", &op);        for(int i = 0; i < n; i++)            scanf("%d", &a[i]);        a[n] = a[n-1] + 1;//假设数组尾部有一个单独的大数        R_rle(n);        while(op--) {            int L, R, ans;            scanf("%d%d", &L, &R);            L--;R--;            if(num[L] == num[R])ans = R - L + 1;//L、R在相同的段内            else {                ans = max(r[L] - L + 1, R - l[R] + 1);//先算出a[L]、a[R]的个数,取最值                if(num[L] + 1 <= num[R] - 1) {int u = R_quer(num[L] + 1, num[R] - 1);//查询这之间的最值ans = max(ans, u);                }            }            printf("%d\n",ans);        }    }    return 0;}


1 0
原创粉丝点击