poj 3386 -- Frequent values (RMQ/线段树)

给一串不降序列,每次询问给出一个区间,求区间元素出现最多的次数。

可以用线段树或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 ) ;
        }
    }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值