Emmmm闲着没事切一道莫队,半小时码+调
感觉自己码力又增加了
BZOJ3585传送门
给出一个长为N的数字串,多次询问区间中没有出现过的自然数的最小值
输入格式:
第一行两个整数N,M,表示数字串长度和询问个数
接下来N个整数表示数字串
再接下来M行,每行两个整数L,R表示一个询问
输出格式:
对于每个询问,输出一行表示答案
这题解法超多= =
这题把输入离散化之后就是BZOJ3339,可以套用3339的线段树做法(当然要加一点特判)
然后还可以用主席树搞,大概是这样:前往Candy?的blog
树套树没有仔细想,感觉行不通
然后就是莫队暴力了=w=
首先把 询问 按照分块之后排序,然后考虑如何维护mex
统计一个数的出现次数是莫队经典操作,复杂度是 移动复杂度*单次修改复杂度,然后还需要能够接受的求mex的复杂度
查询所有数字中没有出现过的最小数字,可以用线段树来搞。这样单次修改复杂度就是log,总体复杂度就是 NN−−√logN ,可能过不去…
那么现在需要寻找一种可以修改O(1),查询O(能接受)的算法。比较容易想到按照 值域 分块,求mex时枚举所有的值域块,再进入第一个没有满的块里暴力查找mex(特判如果当前块是满的,并且起始位置原来的数字已经大于整个序列的mex,答案就是整个序列的mex了),这样单次查询就是 N−−√ 的
UPD at 当天下午:才发现自己撒比了…莫队的做法根本不用离散化…因为mex的值最大是N+1,所以在莫队的时候直接判断,如果当前数字比N大的话就直接不操作= =
/**************************************************************
Problem: 3585
User: Izumihanako
Language: C++
Result: Accepted
Time:6568 ms
Memory:9436 kb
****************************************************************/
#include
#include
#include
#include
using namespace std ;
int N , M , val[200005] , uni[200005] , arc[200005] , uninum , ans[200005] , maxmex ;
int Bsiz , Btot , bel[200005] , vcnt[200005] ;
int Bcnt[500] , fullsiz[500] , st[500] , ed[500] ;
struct Unique_Data{
int num , id ;
bool operator < ( const Unique_Data &A ) const {
return num < A.num ;
}
}Uni[200005] ;
struct Queries{
int lf , rg , id ;
bool operator < ( const Queries &A ) const {
return bel[lf] < bel[A.lf] ||
( bel[lf] == bel[A.lf] && rg < A.rg ) ;
}
}Q[200005] ;
void Unique_(){
sort( Uni + 1 , Uni + N + 1 ) ;
for( int i = 1 , las = -1 ; i <= N ; i ++ ){
if( las != Uni[i].num ){
uninum ++ ;
arc[uninum] = Uni[i].num ;
las = Uni[i].num ;
}
uni[ Uni[i].id ] = uninum ;
}
}
void set_Blo(){
Bsiz = sqrt( N ) ;
Btot = ( N / Bsiz ) + ( bool )( N%Bsiz ) ;
for( int i = 1 ; i <= Btot ; i ++ ){
st[i] = ed[i-1] + 1 ;
ed[i] = min( st[i] + Bsiz - 1 , N ) ;
fullsiz[i] = ed[i] - st[i] + 1 ;
for( int j = st[i] ; j <= ed[i] ; j ++ )
bel[j] = i ;
}
st[Btot+1] = N + 1 ;
ed[Btot+1] = 0x3f3f3f3f ;
fullsiz[Btot+1] = 12345; //必须要设置一个, 不然fullsiz和Bcnt都是0 , 某些数据会GG
}
void init(){
Unique_() ;
set_Blo() ;
for( int i = 1 ; i <= 200001 ; i ++ )
if( arc[i] != arc[i-1] + 1 ){
maxmex = arc[i-1] + 1 ;
break ;
}
sort( Q + 1 , Q + M + 1 ) ;
}
int Query(){
int tmp ;
for( int i = 1 ; i <= Btot ; i ++ ){
if( Bcnt[i] != fullsiz[i] ){
tmp = i ; break ;
}
if( arc[ st[i] ] >= maxmex ) return maxmex ;
}
for( int i = st[tmp] ; i <= ed[tmp] ; i ++ ){
if( i >= maxmex ) return maxmex ;
if( !vcnt[i] ) return arc[i] ;
}
}
void solve(){
int L = 1 , R = 0 ;
for( int i = 1 ; i <= M ; i ++ ){
int lf = Q[i].lf , rg = Q[i].rg ;
while( R < rg ){
R ++ ;
vcnt[ uni[R] ] ++ ;
if( vcnt[ uni[R] ] == 1 ) Bcnt[ bel[uni[R]] ] ++ ;
}
while( R > rg ){
vcnt[ uni[R] ] -- ;
if( vcnt[ uni[R] ] == 0 ) Bcnt[ bel[uni[R]] ] -- ;
R -- ;
}
while( L > lf ){
L -- ;
vcnt[ uni[L] ] ++ ;
if( vcnt[ uni[L] ] == 1 ) Bcnt[ bel[uni[L]] ] ++ ;
}
while( L < lf ){
vcnt[ uni[L] ] -- ;
if( vcnt[ uni[L] ] == 0 ) Bcnt[ bel[uni[L]] ] -- ;
L ++ ;
}
ans[ Q[i].id ] = Query() ;
}
for( int i = 1 ; i <= M ; i ++ )
printf( "%d\n" , ans[i] - 1 ) ;
}
int main(){
scanf( "%d%d" , &N , &M ) ;
for( int i = 1 ; i <= N ; i ++ ){
scanf( "%d" , &val[i] ) ; val[i] ++ ;
Uni[i].num = val[i] , Uni[i].id = i ;
}
for( int i = 1 ; i <= M ; i ++ ){
scanf( "%d%d" , &Q[i].lf , &Q[i].rg ) ;
Q[i].id = i ;
}
init() ;
solve() ;
}