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.
Note: A naive algorithm may not run in time!
Sample Input
10 3 -1 -1 1 1 1 1 3 10 10 10 2 3 1 10 5 10 0
Sample Output
1 4 3
这个题看起来挺简单,然而做起来还是挺难的,我也是看了蓝书上思路之后,自己才写出来的。
分析:
题目要我们求区间里的最大值,这就让我们想到了RMQ,然而,乍一看,怎么也用不了RMQ,所以要转换,而往往转换才是关键,如果能想出转换,那么后面的应该就不难,但是自己智商实在不够。
那么,怎么转换呢,想一下,RMQ中找最大(或最小)的对象是一个范围里面的单独的数,对应本题就是一个范围里面的每个数的次数。那么,我们把相同的数框起来,转化为一个次数,最后得到了它们的次数所组成的一个数组,这时候就可以用RMQ了,我们先把每几个相同的数组成的集合叫做段,输入的数据就被转化为一个一个段了。
但是,还有问题,这么一转化,给的范围就和他们对应不上了,所以我们要给这些段标上号,然后,再让原始数据每一个下标去对应一个段的下标,比如 1 1 2 2 2 3 3, 有三个段,原始数组中,下标为 1,2的数对应的段为1,下标为3,4,5对应段为2.
所以,最后对于题目给的下标范围,l和r,就可以转换为段的范围,比如上例中,题目询问 1 4,那么我们就可以转化为1 段,2段
最后在段中使用RMQ。
还有一个要注意的问题是每次的询问 l和r 不一定覆盖一个段,所以我们给段在加一个元素(struct 里面加元素),边界,即左范围,右范围。 对于覆盖段的,用右范围减去l, 或者 用 r 减去 左范围,(可以画画图想想),然后不覆盖的最好单独列举出来,最后进行比较,选出最大的。
下面是AC代码, 个人觉得尽量不要看别人代码,主要看思路,如果又看思路又看代码,那就浪费了做题的机会了。
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<vector>
int n,k;
const int maxn = 1e5+2;
struct spe
{
int l,r;
int data,total;
};
int indx[maxn];
spe segment[maxn];
int dp[maxn][22];
void rmq_init( int cnt )
{
for( int i = 1 ; i <= cnt ; i++ )
dp[i][0] = segment[i].total;
for( int j = 1 ; ( 1<<j ) <= cnt ; j++ )
for( int i = 1 ; i+(1<<j)-1 <= cnt ; i++ )
dp[i][j] = max( dp[i][j-1], dp[ i + (1<<( j-1 ) ) ][j-1] );
}
int rmq( int l, int r )
{
int i = 1;
while( ( 1<<i ) <= r-l+1 )
i++;
return max( dp[l][ i-1 ] , dp[ r-( 1<<(i-1) )+1 ][i-1] );
}
int main()
{
while( scanf("%d",&n) == 1 && n )
{
scanf("%d",&k);
memset(segment,0,sizeof(segment));
memset(indx,0,sizeof(indx));
memset(dp,0,sizeof(dp));
int item , cnt = 1;
scanf("%d",&item); //给第一个数
segment[cnt].data = item; segment[cnt].l = 1; segment[cnt].total++;
indx[1] = 1; //第一个数在第一段
for( int i = 2 ; i <= n ; i++ )
{
scanf("%d",&item);
if( item == segment[cnt].data )
indx[i] = cnt, segment[cnt].total++;
else
{
segment[cnt].r = i-1;
cnt++;
segment[cnt].l = i;
segment[cnt].data = item;
segment[cnt].total++;
indx[i] = cnt;
}
}
segment[cnt].r = n;
indx[n] = cnt; //最后一个一定是最后一段
rmq_init(cnt);
//******************询问*************************
int a,b;
for( int i = 1 ; i <= k ; i++ )
{
scanf("%d %d",&a,&b);
if( indx[a] == indx[b] )
printf("%d\n",b-a+1);
else if( indx[b] - indx[a] == 1 )
{
int temp = max( segment[ indx[a] ].r - a+1 , b - segment[ indx[b] ].l +1 );
printf("%d\n",temp);
}
else
{
int left = segment[ indx[a] ].r - a+1;
int right = b - segment[ indx[b] ].l + 1;
int temp;
temp = max(left,right);
int t = rmq( indx[a] + 1, indx[b] -1 );
int aa = max(temp,t);
printf("%d\n",aa);
}
}
}
return 0;
}
/*
*/