给一串不降序列,每次询问给出一个区间,求区间元素出现最多的次数。
可以用线段树或RMQ, 我这里用的RMQ ,想了很久,写了很久,WA了很久,小处理有点麻烦,解释在代码中
# include <cstdio>
# include <iostream>
# include <set>
# include <map>
# include <vector>
# include <list>
# include <queue>
# include <stack>
# include <cstring>
# include <string>
# include <cstdlib>
# include <cmath>
# include <algorithm>
using namespace std ;
struct DP
{
int l , r , m ; //l表示区间最左边元素出现的次数,r表示区间最右边元素次数,m表示区间元素出现最多的次数
} dp [ 110000 ] [ 20 ] ;
int a [ 110000 ] ;
int n ;
void getdp ( ) //构建RMQ数组
{
for ( int i = 1 ; i <= n ; i ++ )
{
dp [ i ] [ 0 ] . m = dp [ i ] [ 0 ] . l = dp [ i ] [ 0 ] . r = 1 ;
}
for ( int j = 1 ; j <= 20 ; j ++ )
for ( int i = 1 ; i + ( 1 << j ) - 1 <= n ; i ++ )
{
dp[ i ] [ j ] . m = max (dp[ i ][j - 1] . m ,dp[i+(1<<(j-1))][j-1].m);
dp[ i ] [ j ] . l = dp [ i ] [ j - 1 ] . l ;
dp[ i ] [ j ] . r = dp [ i + (1<<(j-1))] [ j - 1 ] . r ;
if ( a [ i + ( 1 << ( j - 1 ) )-1] == a [ i + (1<<(j-1)) ] ) //区间合并时仅当两区间交界的两个元素相等时才会对 m , l , r 产生影响
{
dp[ i ][ j ].m = max(dp[i][j].m,dp[i][j-1].r+dp[i+(1<<(j-1))][j-1].l) ; //中间元素数量变化后有可能成为最大值
int t = a [ i + ( 1 << ( j - 1 ) ) - 1 ] ;
if ( a [ i + ( 1<<j ) - 1 ] == t ) //如果最右边元素和中间元素相等,那么r会变化 ,下同
{
dp [ i ] [ j ] . r = dp [ i ] [ j ] . m ;
}
if ( a [ i ] == t )
{
dp [ i ] [ j ] . l = dp [ i ] [ j ] . m ;
}
}
}
}
int main ( )
{
while ( scanf ( "%d" , & n ) , n )
{
memset ( dp , 0 , sizeof ( dp ) ) ;
int q ;
scanf ( "%d" , & q ) ;
for ( int i = 1 ; i <= n ; i ++ )
scanf ( "%d" , a + i ) ;
getdp ( ) ;
while ( q -- )
{
int x , y ;
scanf ( "%d%d" , & x , & y ) ;
int len = floor ( log2 ( y - x + 1 ) ) ; //询问阶段
int maxx = max ( dp [x][len].m , dp[ y - (1<<len) +1 ][ len ] . m ) ;
if ( a [ x + (1<<len)-1] == a [ y - (1<<len)+1] )//仅当两区间交界的两个元素相等时才会对 maxx 产生影响
{
int l = (1<<(len+1) )- 2 - ( y - x ) + 1 ; //两段区间重合的长度
int tl = dp [ x ] [ len ] . r + dp [ y - (1<<len)+1][len].l -l; //中间元素的个数
maxx = max ( maxx , tl ) ;
}
printf ( "%d\n" , maxx ) ;
}
}
}