POJ 3368 && HDU 1806 Frequent values(ST)

470 篇文章 3 订阅
15 篇文章 0 订阅

Description
给你一个由n个数字组成的非降序序列,有m次询问,每次询问区间[l,r]之间出现最多的数字出现的次数
Input
第一行两个整数n和m分别表示序列长度和查询次数,第二行n个整数表示该序列,之后m行每行两个整数l和r表示查询区间
Output
对于每次查询,输出区间[l,r]中出现次数最多的数字的次数
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
Solution
该题只涉及查询不涉及修改选用ST算法
我们用数组counts,按顺序统计一个数字连续·出现的次数,这个数字保存在value中(本题不需要保存,如果询问是哪个数则需要),然后用一个结构统计一个下标所在的区间的左右范围,以及当前下标的数字在counts中属于哪个位置,最后对counts进行RMQ初始化,查询区间最大值,
对于l,r的询问
我们ans=max(从l到l所在段的结束处元素数量,从r到r开始出的元素数量,位于l和r之间的最大值(rmq查询))
注意l和r位于一个区间的情况,已经找l,r中间的区间的时候,可能会出现最后L>R的情况。
Code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
#define maxn 100010
struct node
{
    int l,r,p;
}data[maxn];
int n,m,p;
int cnt[maxn];
int value[maxn];
int dp[maxn][60];
void init()
{
    for(int i=0;i<n;i++)
        dp[i][0]=cnt[i];
    int k=(int)(log(n*1.0)/log(2.0));
    for(int j=1;j<=k;j++)
        for(int i=1;i+(1<<j)<=p+1;i++)
            dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
int rmp(int i,int j)
{
    int m=(int)((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,t;
        p=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&t);
            if(t!=v&&l!=0)//按顺序统计一个数字连续出现的次数 
            {
                cnt[p]=r-l+1;
                value[p]=t;//这个数字保存在value中 
                for(int j=l;j<=r;j++)//用一个结构统计一个下标所在的区间的左右范围 
                    data[j].l=l,data[j].r=r,data[j].p=p;
                p++;r++;l=r;v=t;
            }
            else
                r++;
            if(l==0)//第一段连续的数字 
            {l=1;v=t;}
        }
        cnt[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();//初始化RMQ 
        while(m--)
        {
            scanf("%d%d",&l,&r);
            if(data[l].p==data[r].p)//l和r处于同一段 
                printf("%d\n",r-l+1);
            else
            {
                int ll=data[l].p+1;
                int rr=data[r].p-1;
                int ans=data[l].r-l+1;//l到l所在段结束处的元素数量 
                ans=max(ans,r-data[r].l+1);//r到r所在段起始处的元素数量 
                if(ll<=rr)//l和r之间还有其他段 
                    ans=max(ans,rmp(ll,rr));
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值