RMQ-HDU1806 Frequent values


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;
}

/*



*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值