Frequent values(POJ 3368)(ST RMQ)

题目
题意
一个非降序序列,有若干查询,每次查询一个区间中重复次数最多的数字的个数。
思路
因为是非降序的,所以可以从头遍历把每个相同的数字划为一个集合,用p[i]表示ai划分到了哪个块里面,同时还可以记录每个集合的左右边界,同时还可以获得每个集合中数字的个数。可以把这些个数处理成ST表。
对于查询的区间,有三种可能
(1)该区间只有一个集合
那就直接右边界-左边界+1
(2)该区间有两个集合
找出左边界所在集合,在该区间的个数,以及右边界所在集合,在该区间的个数,比较二者大小
(3)该区间有两个以上的集合
先重复(2),找出左右边界集合的最大值之后,在通过ST表查询中间这些集合的最大值,然后把二者比较,就可得出最终最大值
代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdio>
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
typedef long long ll;
typedef pair<int,int> PII;
int F[maxn][20];
int a[maxn];
int n,q;
int Left[maxn],Right[maxn],pos[maxn];
int p;

void ST()
{
    memset(F,0,sizeof(F));
    for(int i=1;i<=p;i++)
        F[i][0]=Right[i]-Left[i]+1;
    int k=log2(p);
    for(int j=1;j<=k;j++)
    {
        for(int i=1;i<=p-(1<<j)+1;i++)
            F[i][j]=max(F[i][j-1],F[i+(1<<(j-1))][j-1]);
    }
}

int query(int l,int r)
{
    int k;
    k=log2(r-l+1);
    return max(F[l][k],F[r-(1<<k)+1][k]);
}

int main()
{

    while(~scanf("%d %d",&n,&q)&&n)
    {
        memset(Left,0,sizeof(Left));
        memset(Right,0,sizeof(Right));
        memset(pos,0,sizeof(pos));
        p=0;
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        a[0]=inf;
        for(int i=1;i<=n;i++)
        {
            if(a[i]!=a[i-1])
            {
                p++;
                pos[i]=p;
                Right[p]=Left[p]=i;
            }
            else
            {
                pos[i]=p;
                Right[p]++;
            }
        }
        ST();
        int from,to;
        while(q--)
        {
            scanf("%d %d",&from,&to);
            int ans;
            if(pos[from]==pos[to])
                ans=to-from+1;
            else if(pos[to]-pos[from]==1)
                ans=max(Right[pos[from]]-from+1,to-Left[pos[to]]+1);
            else
            {
                ans=max(Right[pos[from]]-from+1,to-Left[pos[to]]+1);
                ans=max(ans,query(pos[from]+1,pos[to]-1));
            }
            cout<<ans<<endl;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值