就当作分块的练习题好了,
跪了半个小时,跪在了 log200 的常数上……最后还是解决了这个问题哒……
首先说如何想到分块!分块是一个很神奇的算法!当你看到了40000或者,你认为需要NlogN的复杂度,但是由于需要维护的信息太蛇皮又无法使用线段树主席树树套树Splay或者Treap实现的时候,可以考虑分块哦。
这道题就是很难用线段树实现的,不过好像可以用主席树维护值域线段树,由于太麻烦了( 我也很懒 ),就没有尝试,但是一个主席树的板子就是——算上离散化70多行!还是很吓人哒!
回归正题,如何使用分块完成呢?
整体思路是首先想到如果我们可以将块到块整块的众数维护出来,那么我们只需要去比较其他不为整块的众数但是在整块外面出现过的数出现的次数是否大于众数的个数就好啦!
一个数在前i块出现的次数当然可以用前缀和的思想求出,但是怎么求出块与块之间的众数呢?
首先我们可以想到可以维护每一块的众数,再用它去维护块到块的众数,但是众数是不满足 “结合律” 的,所以我们考虑如何换种方式去统计!如果我们需要得到块到块的众数的话,我们必须要枚举每一个块和它后继的块,这个复杂度是,但是我们还是要考虑如何快速统计块i到块j之间的的众数,我们可以暴力地统计每一个块中的众数,每到一个块k就算上i~k之间的众数并用块k去更新这个众数,开个桶记一下就好了!
这样计算众数就易如反掌了,对于询问区间 [ l , r ] 的众数,如果 l,r 属于同一个块或者相邻的块,我们就可以直接暴力 for 了,否则就分别处理整块和整块外出现的数,这样最坏是的,所以最后的复杂度在级别!
代码:
还有一个小技巧,由于需要离散化,我们必须要每次 lower_bound 查询离散的位置,这样会耗费 的复杂度,超时啦解决的方法是先预处理出离散化数组,将离散化后的值替换掉原来的值,这样就能够 查询了嘿嘿嘿……
// 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 ;
}