HDU1806(Frequent values)

Frequent values

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

1
4
3

思路

线段树维护区间众数模型 + 大批量查询。维护区间众数,区间最长连续子序列,区间最长连续上升子序列都是同一种做法,需要维护三个东西。

  1. ls区间左端点为起点的最大连续长度,rs区间右端点为终点的最大连续长度,ms区间的最大连续长度。

  2. 那么父区间的ls可以由左区间的ls向上合并得到,父区间的rs可以由右区间的rs向上合并得到。当然需要考虑左右区间为满区间的情况,如果相邻两个值相等且左右区间各自为满的情况需要考虑父区间的ls 和 rs跨到另一个区间的问题。父区间的ms可以由左区间的ms,右区间的ms,左区间的rs + 右区间的ls三者最大得到。

  3. 查询的时候先查出左区间最大,再查出右区间最大,最后如果相邻两个值相等的时候需要考虑跨区间的长度可能比左右区间最大还大一点。而且左区间的最大右连续的左端点比查询区间左端点还要远(端点值更小),右区间的最大左连续的右端点比查询区间右端点还要远(端点值更大)两种情况。

最后注意上面几个该有的细节几乎不会出错,具体细节看代码。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
#define lson k<<1,l,m
#define rson k<<1|1,m+1,r
const int maxn = 1e5+5;
struct node{
	int ls;
	int rs;
	int ms;
}s[maxn<<2];
int a[maxn];
inline void push_up(int k,int l,int r)			//向上合并区间 
{
	int m = (l + r) >> 1;
	s[k].ls = s[k<<1].ls;
	s[k].rs = s[k<<1|1].rs;
	s[k].ms = max(s[k<<1].ms,s[k<<1|1].ms);
	if(a[m] == a[m+1]){			//相邻的值相等可能需要进行左右区间合并。
		if(s[k<<1].ms == m-l+1){
			s[k].ls += s[k<<1|1].ls;
		}
		if(s[k<<1|1].ms == r-m){
			s[k].rs += s[k<<1].rs;
		}
		s[k].ms = max(s[k].ms,s[k<<1].rs+s[k<<1|1].ls);
	}
}
inline void build(int k,int l,int r)
{
	if(l == r){
		s[k].ls = s[k].rs = s[k].ms = 1;
		return ;
	}
	int m = (l + r) >> 1;
	build(lson);
	build(rson);
	push_up(k,l,r);
}
int query(int k,int l,int r,int ql,int qr)
{
	if(qr < l || r < ql){
		return 0;
	} 
	if(ql <= l && r <= qr){
		return s[k].ms;
	}
	int m = (l + r) >> 1;
	int s1 = query(lson,ql,qr);
	int s2 = query(rson,ql,qr);
	int ans = max(s1,s2);
	if(a[m] == a[m+1]){		//注意细节,解释在上面
		ans = max(ans,min(s[k<<1].rs,m-ql+1)+min(s[k<<1|1].ls,qr-m));
	}
	return ans;
} 
int main()
{
	int n,m;
	while(~scanf("%d",&n) && n){
		scanf("%d",&m);
		for(int i = 1;i <= n;i++){
			scanf("%d",&a[i]);
		}
		build(1,1,n);
		while(m--){
			int x,y;
			scanf("%d%d",&x,&y);
			int ans = query(1,1,n,x,y);
			printf("%d\n",ans);
		}
	}
	return 0;
} 

愿你走出半生,归来仍是少年~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值