【UVA11235】Frequent values

题意

  有序列{ Sn },并且 S 单调不降
  有Q组询问,每一组询问为 lr 内出现次数最高的数的出现次数
   1nQ106 ,多组数据

解法

线段树:
  又是这种区间合并,要求分类讨论的题目……
  有一个假想的暴力做法:
  因为 S 单调不降,所以可以记录每一个数的左右范围,然后二分一下l在哪个数的范围内,然后往后递推取 max ,直到当前数的右边界大于等于 r ,这样做的极限复杂度是O(lognQi=1rili+1
  如果要改成线段树来做,那么记录的东西也要改变:对于当前线段树上的区间 lr ,记录 len 表示区间内的答案, lz 表示左端点表示的数在区间内的长度, rz 同理
  显然当 l=r 时,三者皆为1,。然后我们就要考虑网上合并:
  初始情况很容易得知: lenk=maxlenlslenrslzk=lzlsrzk=rzrs
  接下来就要考虑怎么合并左右区间:
  ①. Sl=Smid+1 ,那么说明整个左儿子区间都是同一个数,并且和右儿子区间的左半部分相连,所以有: lzk=lzk+lzrs
  ②. Smid=Sr ,那么说明整个右儿子区间都是同一个数,并且和左儿子区间的右半部分相连,所以有: rzk=rzk+rzls
  ③. Smid=Smid+1 ,那么说明区间中间部分是相连的,所以有: lenk=maxlenkrzls+lzrs
  至于询问操作也是差不多的搞法,弄一个结构体,同时返回三个值,然后合并一下再往上回溯,最后的答案就是三者的 max

PS:

  按理说,代码中的 clear 函数加了之后应该变慢的,但是我发现如果加上这个函数只要 170ms ,如果不加就要 260ms ,有大神能解释的就发个评论告诉我呗……

复杂度

O( Tqlogn

代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#define ls 2*k
#define rs 2*k+1
#define Rint register int
#define Lint long long int
using namespace std;
const int N=100010;
int w[N];
int n,q;
struct Tree
{
    struct node
    {
        int l,r;
        int lz,rz,len;
    }t[N*4];
    struct task
    {
        int len,lz,rz;
    }A;
    void build(Rint k,Rint l,Rint r)
    {
        t[k].l=l,t[k].r=r;
        if( l==r )
        {
            t[k].lz=t[k].rz=t[k].len=1;
            return ;
        }
        int mid=(l+r)/2;
        build( ls,l,mid ),build( rs,mid+1,r );
        t[k].len=max( t[ls].len,t[rs].len );
        t[k].lz=t[ls].lz;
        t[k].rz=t[rs].rz;
        if( w[mid]==w[mid+1] )   t[k].len=max( t[k].len,t[ls].rz+t[rs].lz );
        if( w[l]==w[mid+1] )   t[k].lz+=t[rs].lz;
        if( w[mid]==w[r] )   t[k].rz+=t[ls].rz;
    }
    task query(Rint k,Rint l,Rint r)
    {
        if( t[k].l==l && t[k].r==r )   return (task){ t[k].len,t[k].lz,t[k].rz };
        if( r<=t[ls].r )   return query( ls,l,r );
        else
            if( l>=t[rs].l )   return query( rs,l,r );
            else
            {
                task L=query( ls,l,t[ls].r ),R=query( rs,t[rs].l,r );
                task ret=(task){ max( L.len,R.len ),L.lz,R.rz };
                if( w[t[ls].r]==w[t[rs].l] )   ret.len=max( ret.len,L.rz+R.lz );
                if( w[l]==w[t[rs].l] )   ret.lz+=R.lz;
                if( w[t[ls].r]==w[r] )   ret.rz+=L.rz;
                return ret;
            }
    }
    void clear(Rint k,Rint l,Rint r)
    {
        t[k].l=0,t[k].r=0;
        if( l==r )
        {
            t[k].lz=t[k].rz=t[k].len=0;
            return ;
        }
        int mid=(l+r)/2;
        clear( ls,l,mid ),clear( rs,mid+1,r );
    }
}T;
int main()
{
    int l,r;
    while( scanf("%d",&n)!=EOF )
    {
        if( !n )   break ;
        scanf("%d",&q);
        for(int i=1;i<=n;i++)   scanf("%d",&w[i]);
        T.build( 1,1,n );
        while( q-- )
        {
            scanf("%d%d",&l,&r);
            T.A=T.query( 1,l,r );
            printf("%d\n",max( T.A.len,max( T.A.lz,T.A.rz ) ));
        }
        T.clear( 1,1,n );
    }
    return 0;
}
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值