【洛谷】4168 [Violet] 蒲公英

就当作分块的练习题好了,

跪了半个小时,跪在了 log200 的常数上……最后还是解决了这个问题哒……

 

首先说如何想到分块!分块是一个很神奇的算法!当你看到了40000或者,你认为需要NlogN的复杂度,但是由于需要维护的信息太蛇皮又无法使用线段树主席树树套树Splay或者Treap实现的时候,可以考虑分块哦。

这道题就是很难用线段树实现的,不过好像可以用主席树维护值域线段树,由于太麻烦了( 我也很懒 ),就没有尝试,但是一个主席树的板子就是——算上离散化70多行!还是很吓人哒!

 

回归正题,如何使用分块完成呢?

整体思路是首先想到如果我们可以将块到块整块的众数维护出来,那么我们只需要去比较其他不为整块的众数但是在整块外面出现过的数出现的次数是否大于众数的个数就好啦!

一个数在前i块出现的次数当然可以用前缀和的思想求出,但是怎么求出块与块之间的众数呢?

 

首先我们可以想到可以维护每一块的众数,再用它去维护块到块的众数,但是众数是不满足 “结合律” 的,所以我们考虑如何换种方式去统计!如果我们需要得到块到块的众数的话,我们必须要枚举每一个块和它后继的块,这个复杂度是\sqrt{N}*\sqrt{N},但是我们还是要考虑如何快速统计块i到块j之间的的众数,我们可以暴力地统计每一个块中的众数,每到一个块k就算上i~k之间的众数并用块k去更新这个众数,开个桶记一下就好了!

 

这样计算众数就易如反掌了,对于询问区间 [ l , r ] 的众数,如果 l,r 属于同一个块或者相邻的块,我们就可以直接暴力 for 了,否则就分别处理整块和整块外出现的数,这样最坏是2*\sqrt{N}的,所以最后的复杂度在N*\sqrt{N}级别!

 

代码:

 

还有一个小技巧,由于需要离散化,我们必须要每次 lower_bound 查询离散的位置,这样会耗费 log\sqrt{N}\approx 7 的复杂度,超时啦解决的方法是先预处理出离散化数组,将离散化后的值替换掉原来的值,这样就能够 O(1) 查询了嘿嘿嘿……

 

// luogu-judger-enable-o2
# include <bits/stdc++.h>

int  cnt [ 405 ] [ 50005 ] , f [ 405 ] [ 405 ] ;
int  val [ 50005 ] , appr [ 50005 ] , pos [ 50005 ] , bl [ 50005 ] , br [ 50005 ] , buck [ 50005 ] , dot [ 50005 ] ;
int  n , m , block , num , tot , x , y , lastans ; 
bool  tag [ 50005 ] ; 

int  query ( int  l , int  r ) {
    int  Gyx = - 1 , valx = - 1 ;
    memset ( buck , 0 , sizeof ( buck ) ) ;
    memset ( tag , false , sizeof ( tag ) ) ;
    memset ( appr , 0 , sizeof ( appr ) ) ;
    tot = 0 ;
    if ( pos [ l ] == pos [ r ]  ||  ( pos [ l ] == pos [ r ] - 1 ) ) {
        //暴力枚举
        for ( int  i = l ; i <= r ; i ++ ) {
            int  plc = val [ i ] ;
            buck [ ++ tot ] = plc ;
        }
        for ( int  i = 1 ; i <= tot ; i ++ )
            appr [ buck [ i ] ] ++ ;
        for ( int  i = 1 ; i <= tot ; i ++ )
            if ( ! tag [ buck [ i ] ] ) {
                tag [ buck [ i ] ] = true ;
                if ( appr [ buck [ i ] ] > valx  ||  appr [ buck [ i ] ] == valx  &&  Gyx > buck [ i ] )
                    valx = appr [ buck [ i ] ] , Gyx = buck [ i ] ;
            }
        return  dot [ Gyx ] ; 
    }
    else {
        //硬上分块
        int  lx = pos [ l ] , rx = pos [ r ] ;
        Gyx = f [ lx + 1 ] [ rx - 1 ] ;
        valx = cnt [ rx - 1 ] [ Gyx ] - cnt [ lx ] [ Gyx ] ;
        for ( int  i = l ; i <= br [ lx ] ; i ++ ) {
            int  plc = val [ i ] ;
            buck [ ++ tot ] = plc ;
        }
        for ( int  i = bl [ rx ] ; i <= r ; i ++ ) {
            int  plc = val [ i ] ;
            buck [ ++ tot ] = plc ;
        }
        for ( int  i = 1 ; i <= tot ; i ++ )
            appr [ buck [ i ] ] ++ ;
        
        valx += appr [ Gyx ] ;
        
        for ( int  i = 1 ; i <= tot ; i ++ )
            if ( ! tag [ buck [ i ] ] ) {
                tag [ buck [ i ] ] = true ;
                if ( cnt [ rx - 1 ] [ buck [ i ] ] - cnt [ lx ] [ buck [ i ] ] + appr [ buck [ i ] ] > valx  ||  cnt [ rx - 1 ] [ buck [ i ] ] - cnt [ lx ] [ buck [ i ] ] + appr [ buck [ i ] ] == valx  &&  Gyx > buck [ i ] )
                    valx = cnt [ rx - 1 ] [ buck [ i ] ] - cnt [ lx ] [ buck [ i ] ] + appr [ buck [ i ] ] , Gyx = buck [ i ] ;
            }
        return  dot [ Gyx ] ;
    }
}

int  main ( ) {
    scanf ( "%d%d" , & n , & m ) ;
    block = ( int ) sqrt ( n ) ;

    for ( int  i = 1 ; i <= n ; i ++ )
        scanf ( "%d" , & val [ i ] ) , dot [ i ] = val [ i ] ;

    std :: sort ( dot + 1 , dot + n + 1 ) ;
    num = std :: unique ( dot + 1 , dot + n + 1 ) - dot - 1 ;
    
    for ( int  i = 1 ; i <= n ; i ++ )
    	val [ i ] = std :: lower_bound ( dot + 1 , dot + num + 1 , val [ i ] ) - dot ;
    
    for ( int  i = 1 ; i <= n ; i ++ ) {
        pos [ i ] = ( i - 1 ) / block + 1 ;
        if ( ! bl [ pos [ i ] ] )
            bl [ pos [ i ] ] = i ;
        br [ pos [ i ] ] = i ;
    }

    for ( int  i = 1 ; i <= n ; i ++ ) 
        cnt [ pos [ i ] ] [ val [ i ] ] ++ ;
    for ( int  i = 1 ; i <= pos [ n ] ; i ++ ) {
        for ( int  j = 1 ; j <= num ; j ++ )
            cnt [ i ] [ j ] += cnt [ i - 1 ] [ j ] ;
    }
    for ( int  i = 1 ; i <= pos [ n ] ; i ++ ) {
        memset ( buck , 0 , sizeof ( buck ) ) ;
        int  cur = 0 ;
        for ( int  j = i ; j <= pos [ n ] ; j ++ ) {
            for ( int  k = bl [ j ] ; k <= br [ j ] ; k ++ ) {
                int  plc = val [ k ] ;
                buck [ plc ] ++ ;
                if ( buck [ plc ] > buck [ cur ]  ||  ( buck [ plc ] == buck [ cur ]  &&  cur > plc ) )
                    cur = plc ;
            }
            f [ i ] [ j ] = cur ;
        }
    }
    lastans = 0 ;
    for ( int  i = 1 ; i <= m ; i ++ ) {
        scanf ( "%d%d" , & x , & y ) ;
        int  l = ( x + lastans - 1 ) % n + 1 ;
        int  r = ( y + lastans - 1 ) % n + 1 ;
        if ( l > r )
            l ^= r ^= l ^= r ;
        lastans = query ( l , r ) ;
        printf ( "%d\n" , lastans ) ;
    }
    return  0 ;
} 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值