rmq算法——ST HDU——1806


对应HDU题目:点击打开链接


Frequent values
Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u
Submit  Status

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 andj (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

写得很好。。。 点击打开链接


题意:求某区间内众数的个数


 
/* 1  2 3 4 5 6 7 8  9  10
  -1 -1 1 1 1 1 3 10 10 10

因为是非递减,故可以这样处理:

-1 -1 为一块,即fa[ 1~2 ]=1;
1 1 1 1 为一块,即fa[ 3~6 ]=2;
3 为一块,即fa[7]=3;
10 10 10 为一块,即fa[ 8~10 ]=4;

设询问范围为(l,r)

1.如果fa[l]==fa[r],结果直接就是r-l+1;

2.如果fa[r]-fa[l]==1,表示区域相邻,就分别求右边区域个数跟左边区域个数,取大值

3.如果fa[r]-fa[l]>1,表示区域相差不少于1个,就分别求最右边区域个数,最左边区域个数跟中间区域个数中的最大值,取大值

*/

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
using namespace std;
const int MAXN=100000+10;
const int INF=1<<30;
int fa[MAXN];
int dp[MAXN][20]; //dp[i][j]表示从第i个数起连续2^j个数的最大值
int n,m,u,maxn;

struct Block
{
	int num,left,right,count;
}block[MAXN];

void input()
{
	u=1;
	int x;
	scanf("%d", &x);
	int front=x; //这块区域的数是front
	fa[1]=u; //第一个数属于第一块区域(这里u==1)
	block[u].num=x; //第一个数是什么
	block[u].left=1; //第一块区域最左边是从1开始
	block[u].count=1; //第一块区域有多少个数
	for(int i=2; i<=n; i++){
		scanf("%d", &x);
		if(x==front){ //x还是属于这块区域
			block[u].count++; 
			fa[i]=u; //第i个数属于第u块区域
		}
		else{ //x不属于这块区域
			block[u].right=i-1; //u区域最右边是第i-1个数
			block[++u].left=i; //新区域最左边是第i个数
			block[u].num=x;
			block[u].count=1;
			front=x;
			fa[i]=u;
		}
	}
	block[u].right=n; //最后一个区域最右边的数是第n个数
}

void caldp()
{
	for(int i=1; i<=u; i++){
		dp[i][0]=block[i].count;
	}
	for(int j=1; j<=20; j++){ //注意这个要在外层
		for(int i=1; i<=u; i++){
			if(i+(1<<j)-1 <= u){
				dp[i][j]=max(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
			}
		}
	}
}

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

int main()
{
	//freopen("in.txt","r",stdin);
	while(scanf("%d%d", &n,&m), n)
	{
		memset(dp,0,sizeof(dp));
		input();
		caldp();
		while(m--)
		{
			int l,r;
			scanf("%d%d", &l,&r);
			if(fa[r]==fa[l]){ //属于同一区域
				printf("%d\n", r-l+1);
				continue;
			}
			if(fa[r]-fa[l]==1){  //区域相邻
				int rr=r-block[fa[r]].left+1; //右区域个数
				int ll=block[fa[l]].right-l+1; //左区域个数
				printf("%d\n", rr>ll?rr:ll); //取大的那边
				continue;
			}
			//相隔不小于一个区域
			int L=fa[l], R=fa[r]; //L,R分别为左边和右边那块区域
			int rr=r-block[R].left+1;
			int ll=block[L].right-l+1;
			maxn=rmq(L+1, R-1); //中间那些区域的最大值
			//比较三者取最大值
			int M=rr>ll?rr:ll;
			M=M>maxn?M:maxn;
			printf("%d\n", M);
		}
	}
	return 0;
}



你可能感兴趣的:(rmq算法——ST HDU——1806)