POJ3368 Frequent values【RMQ】

 Frequent values

Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 23566 Accepted: 8252

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 nintegers 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 10
2 3
1 10
5 10
0

Sample Output

1
4
3

Source

Ulm Local 2007

题意给出有n个数的非递减数组,Q个询问[l,r],对于每个询问求出[l,r]之间连续出现次数最多的次数。

思路

RMQ模板题,由于对 [l,r] 的查询会截断端点处的连续性,因此需要进行一下处理,我们用f[i]表示位置i及i前面a[i-1],a[i-2]...有多少个与a[i]相同的数(连续),

例如

a 数组

-1 -1 0 1 3 3 3

f 数组为

1 2 1 1 1 2 3

这样处理后,我们使用ST表记录f数组的区间最值,这样的话右端点不会受到影响,就可以只考虑左端点了,我们要单独计算左端的连续数的数量,使用query函数查询剩余区间的最值,最后取最大值就行了。

C++程序

#include<iostream>
#include<algorithm>
#include<cmath>

using namespace std;

const int N=100010;


//a存放原数组,f[i]表示从a[i]开始a[i],a[i-1],a[i-2]...有多少个与a[i]相同的数(连续)
//dp表示st表 
int a[N],f[N],dp[N][21];

void ST(int n)
{
	for(int i=1;i<=n;i++) dp[i][0]=f[i];
	for(int j=1;(1<<j)<=n;j++)
	  for(int i=1;i+(1<<j)-1<=n;i++)
	    dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}

int query(int l,int r)
{
	if(l>r) return 0;
	int k=(int)(log((double)(r-l+1))/log(2.0));
	return max(dp[l][k],dp[r-(1<<k)+1][k]);
}

int main()
{
	int n,q,x,k,pre,l,r;
	while(~scanf("%d",&n)&&n)
	{
		scanf("%d",&q);
		a[0]=-100100,f[0]=0;//保证a[0]不等于a[1] 
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			if(a[i]==a[i-1])//如果连续
			  f[i]=f[i-1]+1;
			else//如果不连续 
			  f[i]=1;
		}
		ST(n);
		while(q--)
		{
			scanf("%d%d",&l,&r);
			//由于左边连续的数字可能被截断了,因此要单独统计左端点元素在查询区间
			//连续出现的次数
			int tmp=0,k=l;
			while(l<=r&&a[l]==a[k]) l++,tmp++; 
			printf("%d\n",max(tmp,query(l,r)));
		} 
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值