题解 SP1684 【FREQUENT - Frequent values】

题目链接:点击打开链接

题目大意:

给你一段连续的递增序列,然后多次查询,每次查询输出查询区间内相同的数出现的最多的次数。

解题思路:

学校内部的比赛碰到的题目,题目看起来异常的简单,基本确定是线段树。刚开始就是想不出来维护的办法,甚至想到用map储存每个点出现次数吧。后来突然想到了什么左连续 最长右连续最长和区间最长连续。思路顿开,遂 A A A

线段树每个节点维护左连续最长,右连续最长,和区间连续最长,从下往上推。 p u s h u p pushup pushup过程不算太难,就是注意左区间右连续和右区间左连续合并要注意值相等才行。剩下可能就查询麻烦一些了,查询的话如果左区间右端点和右区间左端点相同是要加上一种查询情况的,最后取查询出来的最大值就好。细节看代码,

#include<bits/stdc++.h>
using namespace std;
struct tree{
    int l,r,mid,ls,rs,ms;
}t[500000];
int n,m,a[200000];
void pushup(int rt){     //从下往上更新
    if((t[rt<<1].ls==t[rt<<1].r-t[rt<<1].l+1)&&a[t[rt<<1].r]==a[t[rt<<1|1].l]){
        t[rt].ls=t[rt<<1].ls+t[rt<<1|1].ls;
    }else{
        t[rt].ls=t[rt<<1].ls;
    }
    if((t[rt<<1|1].rs==t[rt<<1|1].r-t[rt<<1|1].l+1)&&(a[t[rt<<1|1].l]==a[t[rt<<1].r])){
        t[rt].rs=t[rt<<1|1].rs+t[rt<<1].rs;
    }else{
        t[rt].rs=t[rt<<1|1].rs;
    }
    t[rt].ms=max(t[rt<<1].ms,t[rt<<1|1].ms);
    if(a[t[rt<<1].r]==a[t[rt<<1|1].l]){      //只有相等才能合并
        t[rt].ms=max(t[rt].ms,t[rt<<1].rs+t[rt<<1|1].ls);
    }
}
void build(int l,int r,int rt){      //建立线段树
    int mid=(l+r)>>1;
    t[rt].l=l;
    t[rt].r=r;
    t[rt].mid=mid;
    if(l==r){
        t[rt].ls=1;
        t[rt].rs=1;
        t[rt].ms=1;
        return;
    }
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
}
int query(int p,int q,int rt){       //查询稍微麻烦一些
    if(t[rt].l==p&&t[rt].r==q){
        return t[rt].ms;
    }
    if(q<=t[rt].mid){
        return query(p,q,rt<<1);
    }
    if(p>t[rt].mid){
        return query(p,q,rt<<1|1);
    }else{
        int aa=query(p,t[rt].mid,rt<<1);
        int bb=query(t[rt].mid+1,q,rt<<1|1);
        int cc=-1;
        if(a[t[rt<<1].r]==a[t[rt<<1|1].l]){      //特判情况
            cc=min(t[rt<<1].rs,t[rt].mid-p+1)+min(t[rt<<1|1].ls,q-t[rt].mid);
        }
        return max(max(aa,bb),cc);
    }
}
int main(){
    while(scanf("%d",&n)!=EOF){
        if(n==0){
            break;
        }
        scanf("%d",&m);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        build(1,n,1);
        int p,q;
        for(int i=0;i<m;i++){
            scanf("%d%d",&p,&q);
            int g=query(p,q,1);
            printf("%d\n",g);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值