第四届“图灵杯”NEUQ-ACM程序设计竞赛 E: 简单的RMQ【线段树】

问题 E: 简单的RMQ

时间限制: 2 Sec   内存限制: 64 MB
提交: 914   解决: 158
[ 提交][ 状态][ 讨论版]

题目描述

给定一个数组,其中的元素满足非递减顺序。任意给定一个区间[i,j],求其中某个元素重复出现的最大次数。

输入

多组数据输入。每组数据的第一行包含两个整数n和q(1<=n,q<=100000),下一行包含n个整数a1,...,an(-100000<=ai<=100000,i∈{1,...,n}),用空格分隔,数列是升序的(ai<=ai+1)。接下来的q行,每行包含两个整数i和j(1<=i<=j<=n),表示给定区间[i,j]。
输入结束于0(自成一行)。

输出

对输入的q个区间,每个区间输出一个整数表示该区间内重复最多的元素出现的次数,用换行分隔。

样例输入

10 3
-1 -1 1 1 1 1 3 10 10 10
2 3
1 10
5 10
0

样例输出

1
4
3

思路:


问题保证数据递增,那么我们这里将数据离散化之后,暴力查询下标即可。

在查询之前,我们需要将和a【L】相同的左边的数的个数去掉,以及将和a【R】相同的右边的数的个数去掉。

那么我们维护一个数组num【i】,表示num【i】是这一串连续相等的数字中第几个。

并且统计好cont【i】表示离散化之后编号为i的数字的个数即可。

那么删除的左边的元素的个数就是num【i】-1.

那么删除的右边的元素的个数就是cont【i】-num【i】;


过程线段树维护一下。

注意该OJ没有开O2加速,所以Map会TLE,数据离散化用数组标记吧= =



Ac代码:

#include<stdio.h>
#include<string.h>
#include<map>
using namespace std;
#define lson l,m,rt*2
#define rson m+1,r,rt*2+1
int tree[1000050*8];
int a[150000];
int num[150000];
void pushup(int rt)
{
    tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}
int Query(int L,int R,int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
        return tree[rt];
    }
    else
    {
        int m=(l+r)>>1;
        int ans=0;
        if(L<=m)
        {
            ans=max(Query(L,R,lson),ans);
        }
        if(m<R)
        {
            ans=max(Query(L,R,rson),ans);
        }
        return ans;
    }
}
void build( int l ,int r , int rt )
{
    if( l == r )
    {
        tree[rt]=0;
        return ;
    }
    else
    {
        int m = (l+r)>>1 ;
        build(lson) ;
        build(rson) ;
        pushup(rt) ;
    }
}
void update(int p,int c,int l,int r,int rt)
{
    if(l==r)
    {
        tree[rt]+=c;
    }
    else
    {
        int m=(l+r)>>1;
        if(p<=m) update(p,c,lson);
        else  update(p,c,rson);
        pushup(rt);
    }
}
int s[350000];
int cont[350000];
int main()
{
    int n,q;
    while(~scanf("%d",&n))
    {
 
        if(n==0)break;
        scanf("%d",&q);
        int cnt=0;
        memset(s,0,sizeof(s));
        memset(cont,0,sizeof(cont));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(i==0||a[i]!=a[i-1])
            {
                s[i]=++cnt;
                num[i]=1;
            }
            else num[i]=num[i-1]+1,s[i]=s[i-1];
            cont[s[i]]++;
        }
        build(1,n,1);
        for(int i=1;i<=n;i++)update(s[i],1,1,n,1);
        for(int i=0;i<q;i++)
        {
            int l,r;
             scanf("%d%d",&l,&r);
             update(s[l],1-num[l],1,n,1);
             update(s[r],-(cont[s[r]]-num[r]),1,n,1);
             printf("%d\n",Query(s[l],s[r],1,n,1));
             update(s[l],-(1-num[l]),1,n,1);
             update(s[r],(cont[s[r]]-num[r]),1,n,1);
        }
    }
    return 0;
}








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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值