poj 3368 Frequent values(RMQ问题)

再搞一道RMQ吧。。
题意是 给出序列 非降序的 询问q i j 要i 和 j之间的 出现次数最多的次数 就是最高次数
n q 都是10w
这个题目啊我们用游程长度编码 把原来的数组分段   最近才在科学导论的数据压缩里看过的。。
value[i] 表示 第i断的值 count[i]表示第i断的值 出现的次数。

num[p]位置p所在断的编号
left[p]表示位置p所在断的起点
right[p]表示位置p所在断的终点

我们对于每个查询 L R
把L R 分成三段 求这三段的最大次数
第一个是从L开始到这个段的终点的次数 right[L]-L+1
第二个是最后半段 就是 R-left[R]+1

终点那个就是完整的断了 也即是rmq问题 num[L]+1 断开始 到 num[R]-1 这些段的count的最大值。

代码还没写。。。今天肯定写好贴上来

开始写了 发现有些东西不太好处理。。

正好在12点前过了。。。
wa了一次 因为忽略了 l和 r在一个断的情况。
#include<iostream>
#include<cstdio>
#include<cstring>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;

const int maxn=100005;
int arr[maxn];//存这个数组
int value[maxn];// 存每一段的值
int Count[maxn];// 存每一段那个值一共出现的次数
int num[maxn];//从 原来下标i在第几个断
int Left[maxn];// 存原来下标i在的那个断的 左端点
int Right[maxn];// 存原来那个下标i在的那个断的右端点
int n,q,k;
int d[maxn][20];

void rmq_init()
{
    int i,j;
    for(i=1;i<=k;++i)
    d[i][0]=Count[i];
    for(j=1;(1<<j)<=k;++j)
    {
        for(i=1;i+(1<<(j-1))<=k;++i)
        {
            d[i][j]=max(d[i][j-1],d[i+(1<<(j-1))][j-1]);
        }
    }
}

int rmq_query(int l,int r)
{
    int kk=0;
    while((1<<(kk+1))<=r-l+1)
    kk++;
    return max(d[l][kk],d[r-(1<<kk)+1][kk]);
}

int main()
{
    int i,j;
    while(scanf("%d",&n),n)
    {
        memset(Count,0,sizeof(Count));
        memset(Left,0,sizeof(Left));
        memset(Right,0,sizeof(Right));
        scanf("%d",&q);
        for(i=1;i<=n;++i)//先把这个数组输进来
            scanf("%d",&arr[i]);
        k=0;
        arr[0]=arr[1]+1;//让数组中第一个元素 和左边的不相等。
        for(i=1;i<=n;++i)
        {
            if(arr[i]==arr[i-1])
                Count[k]++;
            else//开始新的一段
            {
                k++;
                value[k]=arr[i];
                Count[k]++;
                Left[i]=i;
                Right[i-1]=i-1;
            }
            num[i]=k;//这个时候i的断肯定是k
        }
        //已测试过 到这里 value数组 和count数组已经搞定了 从1到k 一共k断。
        //接下来得搞定num left right 三个数组了
        Right[n]=n;
        int L,R;
        for(i=1;i<=n;++i)
        {
            if(!Left[i])
            Left[i]=L;
            else
            L=Left[i];
        }
        for(i=n;i>=0;--i)
        {
            if(!Right[i])
            Right[i]=R;
            else
            R=Right[i];
        }
/*
        for(i=1;i<=n;++i)
        {
            printf("%d %d %d %d %d\n",value[i],Count[i],num[i],Left[i],Right[i]);
        }*/
        //搞定初始化数组的问题
        rmq_init();
        int ans;
        for(i=0;i<q;++i)
        {
            scanf("%d %d",&L,&R);
            if(num[L]==num[R])
            ans=R-L+1;
            else
            ans=max(Right[L]-L+1,R-Left[R]+1);
            if(num[R]-1>=num[L]+1)
            ans=max(ans,rmq_query(num[L]+1,num[R]-1));
            printf("%d\n",ans);
        }
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值