UVA 11235 Frequent values(RMQ)

本文介绍了一种针对非递增数组的查询优化方法,利用数组特性减少重复计算,通过预处理实现快速查询区间内出现次数最多的数的频次。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:给你一个长度为n的,非递减的数组,现在有q次询问,每次询问输出[l, r]中出现次数最多的那个数的出现次

数。(n, q, |a[i]| <= 1e5)


思路:第一反应是主席树或者莫队搞,写了发莫队n*sqrt(n)*log(n), 6e8,T了。。。注意给你的序列的是非递减的。所以我们可以将相同的数合并为1块,如1 1 1 2 2可以表示成(1, 3), (2, 2)两块,表示1有3个,2有2个。现在可以开这些数组,id[i]表示第i个数在第几块,len[i]表示第i块的长度,lef[i]表示第i块的左端点,rig[i]表示第i块的右端点。

对于每次询问要求的就是下面三者中最大的
1.从L到L所在的块的右端点的元素个数:rig[L]-L+1
2.从R到R所在的段的左端点的元素个数:R-lef[R]+1
3.中间第num[L]+1段到第num[R]-1段的cnt的最大值(RMQ)
对于1, 2先特判L, R是否在一个块内,如在一个块内直接是R-L+1.这样保证了左右端点一定在询问区间内。


代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
int a[maxn], id[maxn], lef[maxn], rig[maxn], len[maxn];
int n, q, cnt;

int mm[maxn], dpMax[maxn][20];
void initRMQ()
{
    for(int i = 1; i <= cnt; i++)
        dpMax[i][0] = len[i];
    for(int j = 1; j <= mm[n]; j++)
        for(int i = 1; i+(1<<j)-1 <= n; i++)
            dpMax[i][j] = max(dpMax[i][j-1], dpMax[i+(1<<(j-1))][j-1]);
}

int RMQ(int l, int r)
{
    int k = mm[r-l+1];
    return max(dpMax[l][k], dpMax[r-(1<<k)+1][k]);
}

int main(void)
{
    mm[0] = -1;
    for(int i = 1; i < maxn; i++)
        mm[i] = ((i&(i-1))==0) ? mm[i-1]+1 : mm[i-1];
    while(cin >> n, n)
    {
        scanf("%d", &q);
        for(int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        cnt = 1;
        id[1] = 1;
        lef[1] = 1;
        rig[1] = 1;
        len[1] = 1;
        for(int i = 2; i <= n; i++)
        {
            if(a[i] == a[i-1])
            {
                id[i] = id[i-1];
                len[id[i]]++;
                rig[id[i]] = i;
            }
            else
            {
                id[i] = ++cnt;
                lef[id[i]] = i;
                rig[id[i]] = i;
                len[id[i]] = 1;
            }
        }
        initRMQ();
        while(q--)
        {
            int l, r;
            scanf("%d%d", &l, &r);
            if(id[l] == id[r])
                printf("%d\n", r-l+1);
            else
            {
                int ans = 0;
                if(id[l]+1 <= id[r]-1)
                    ans = RMQ(id[l]+1, id[r]-1);
                ans = max(ans, max(rig[id[l]]-l+1, r-lef[id[r]]+1));
                printf("%d\n", ans);
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值