第四届“图灵杯”NEUQ-ACM程序设计竞赛(团队赛)-网络同步赛E(线段树)

问题 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
题解:先预处理出每段的长度和结束的点,建线段树维护最大值,对于每次询问l,r。先暴力找到结束点,得到ll rr,然后判断大小即可。(这SB写法都能过?数据是有多水。。。下面有优化)
代码;

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<vector>
#include<string.h>
//#define ll long long
using namespace std;
const int N=1e5+10;
const int mod=1e7+9;
int n,q,l,r;
int a[N];
int sum[N<<2];
void pushup(int rt)
{
    sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
}
void update(int l,int r,int pos,int val,int rt)
{
    if(l==r)
    {
        sum[rt]=val;
        return ;
    }
    int mid=(r+l)>>1;
    if(pos<=mid)
    update(l,mid,pos,val,rt<<1);
    if(pos>mid)
    update(mid+1,r,pos,val,rt<<1|1);
    pushup(rt);
}
int query(int l,int r,int ll,int rr,int rt)
{
    if(ll<=l&&rr>=r)
    {
        return sum[rt];
    }
    int mid=(r+l)>>1;
    int ans=1;
    if(ll<=mid)
    {
        ans=max(ans,query(l,mid,ll,rr,rt<<1));
    }
    if(rr>mid)
    {
        ans=max(ans,query(mid+1,r,ll,rr,rt<<1|1));
    }
    return ans;
}
int main()
{
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        memset(sum,0,sizeof(sum));
        scanf("%d",&q);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        int ans=1;
        int tmp=a[1];
        for(int i=2;i<=n;i++)
        {
            if(a[i]!=tmp)
            {
                update(1,n,i-1,ans,1);
                ans=1;
                tmp=a[i];
            }
            else
            {
                ans++;
            }
        }
        while(q--)
        {
            scanf("%d%d",&l,&r);
            if(a[l]==a[r])
            {
                printf("%d\n",r-l+1);
            }
            else
            {
                int mx=1;
               ans=0;
               tmp=a[l];
               int ll,rr;
                for(int i=l+1;i<=r;i++)
                {
                    if(a[i]!=tmp)
                    {
                        ans=max(ans,mx);
                        ll=i;
                        break;
                    }
                    else
                    mx++;
                }
                tmp=a[r];
                mx=1;
                for(int i=r-1;i>=l;i--)
                {
                     if(a[i]!=tmp)
                    {
                        ans=max(ans,mx);
                        rr=i;
                        break;
                    }
                    else
                    mx++;
                }
                ans=max(ans,query(1,n,ll,rr,1));
                printf("%d\n",ans);
        }

    }

}
}

很明显,上面SB写法最坏时间复杂度为o(qn)这复杂度过个p题啊。改法:对于找边界,我们不要暴力,而是将每段的边界先预处理出来,然后直接判断就好。
代码:

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<vector>
#include<string.h>
//#define ll long long
using namespace std;
const int N=1e5+10;
const int mod=1e7+9;
int n,q,l,r;
int a[N];
int sum[N<<2];
int belong[N];
int L[N],R[N];
void pushup(int rt)
{
    sum[rt]=max(sum[rt<<1],sum[rt<<1|1]);
}
void update(int l,int r,int pos,int val,int rt)
{
    if(l==r)
    {
        sum[rt]=val;
        return ;
    }
    int mid=(r+l)>>1;
    if(pos<=mid)
    update(l,mid,pos,val,rt<<1);
    if(pos>mid)
    update(mid+1,r,pos,val,rt<<1|1);
    pushup(rt);
}
int query(int l,int r,int ll,int rr,int rt)
{
    if(ll<=l&&rr>=r)
    {
        return sum[rt];
    }
    int mid=(r+l)>>1;
    int ans=1;
    if(ll<=mid)
    {
        ans=max(ans,query(l,mid,ll,rr,rt<<1));
    }
    if(rr>mid)
    {
        ans=max(ans,query(mid+1,r,ll,rr,rt<<1|1));
    }
    return ans;
}
int main()
{
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        memset(sum,0,sizeof(sum));
        scanf("%d",&q);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        int ans=1;
        int tmp=a[1];
        int cnt=1;
        L[cnt]=1;
        belong[1]=cnt;
        for(int i=2;i<=n;i++)
        {
            if(a[i]!=tmp)
            {
                R[cnt]=i-1;
                cnt++;
                L[cnt]=i;
                belong[i]=cnt;
                update(1,n,i-1,ans,1);
                ans=1;
                tmp=a[i];
            }
            else
            {
                belong[i]=cnt;
                ans++;
            }
        }
        while(q--)
        {
            scanf("%d%d",&l,&r);
            if(a[l]==a[r])
            {
                printf("%d\n",r-l+1);
            }
            else
            {
               ans=0;
               ans=max(ans,R[belong[l]]-l+1);
               ans=max(ans,r-L[belong[r]]+1);
                ans=max(ans,query(1,n,R[belong[l]]+1,L[belong[r]]-1,1));
                printf("%d\n",ans);
        }

    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值