POJ3368 Frequent values ST算法

本文介绍了一种针对非降序序列的区间查询最频值算法实现,利用RMQ(Range Minimum Query)中的ST算法来高效求解指定区间内出现频率最高的数值及其出现次数。通过预处理统计每个数值连续出现的次数,并构建数据结构辅助快速查询。


给你一个由n个数字组成的非降序序列(这是重要条件)。

有m次询问,每次询问区间【L,R】之间出现最多的数字出现的次数。

该题只涉及查询不涉及修改选用RMQ中的ST算法

我们用数组counts,按顺序统计一个数字连续·出现的次数,这个数子保存在value中(本题其实不需要保存,如果询问是哪个数则需要)

然后用一个结构统计一个下标所在的区间的左右范围,以及当前下标的数字在counts中属于哪个位置。

然后对counts进行RMQ初始化,查询区间最大值,

对于l,r的询问

我们ans=max(从l到l所在段的结束处元素数量,从r到r开始出的元素数量,位于l和r之间的最大值(rmq查询));

注意l和r位于一个区间的情况,已经找l,r中间的区间的时候,可能会出现最后L>R的情况。

Frequent values
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 11241 Accepted: 4110

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

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>

using namespace std;

struct node
{
    int p,l,r;
}data[100100];
int p;
int value[100100];
int counts[100100];
int dp[100050][60];
int n,m;

void init()
{
	for(int i=1;i<=p;i++)
        dp[i][0]=counts[i];
	int k=floor(log((double)(n+1))/log(2.0));
	int m;
	for(int j=1;j<=k;j++)
		for(int i=1;i+(1<<j)-1<=p;i++)
		{
			m=i+(1<<(j-1));
			dp[i][j]=max(dp[i][j-1],dp[m][j-1]);
		}
}

int rmq(int i,int j)
{
	int m=floor((log((double)(j-i+1))/log(2.0)));
	int x=max(dp[i][m],dp[j-(1<<m)+1][m]);
	return x;
}


int main()
{
    while(~scanf("%d",&n)&&n)
    {
        scanf("%d",&m);
        int l=0,r=0,v=0,tmp;
        p=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&tmp);
            if(tmp!=v && l!=0)
            {
                counts[p]=r-l+1;
                value[p]=v;
                for(int j=l;j<=r;j++)
                    data[j].l=l,data[j].r=r,data[j].p=p;
                p++;
                r++;
                l=r;
                v=tmp;
            }
            else
                r++;
            if(l==0)
            {
                l=1;
                v=tmp;
            }
        }
        counts[p]=r-l+1;
        value[p]=v;
        for(int j=l;j<=r;j++)
            data[j].l=l,data[j].r=r,data[j].p=p;
        init();
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&l,&r);
            int L,R;
            if(data[l].p==data[r].p)
                printf("%d\n",r-l+1);
            else
            {
                L=data[l].p+1;
                R=data[r].p-1;
                int ans=data[l].r-l+1;
                ans=max(ans,r-data[r].l+1);
                if(L<=R)ans=max(ans,rmq(L,R));
                printf("%d\n",ans);
            }
        }
    }
	return 0;
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值