[BZOJ4241]-历史研究-回滚莫队

说在前面

之前,naive的me以为回滚莫队是和主席树类似的(支持回退嘛),然后一直没敢写过
直到写了这道题,才知道回滚莫队几乎接近暴力= =…

=w=顺便成功把这个题刷进9s,优化技巧++
这里写图片描述


题目

BZOJ4241传送门

题目大意

给定一个长为N的数列,有Q次询问
每次询问[L,R]中,”各个数字的出现次数 该数字”的最大值

输出输出格式

输入格式:
第一行两个空格分隔的整数N和Q,含义如题
接下来一行N个空格分隔的整数X1…XN,描述这个数列
接下来Q行,第i行(1<=i<=Q)有两个空格分隔整数Ai和Bi,表示第i次询问的区间为[Ai,Bi]。

输出格式:
输出Q行,第i行(1<=i<=Q)一个整数,表示第i次询问的最大重要度


解法

这个题普通莫队貌似不太好做,添加操作虽然很快,但是删除操作时间复杂度太高了…
于是回滚莫队就出现了,因为删除操作时间复杂度高,所以用添加操作去顶替

具体实现:
询问的排序方法,和普通莫队排序一样
如果询问的L和R在同一块内,直接暴力处理
如果询问的L和R不在同一块,那么对于L所在块相同的询问,排序之后R是单增的,因此右端点只会一直向右加入元素,更新答案。L所在块的那一小部分直接暴力处理

时间复杂度上限是NN的。由于处理询问的方式和普通莫队不同,实际速度会有差异
话说,是不是貌似普通莫队可以做的,回滚也可以做来着….


下面是自带大常数的代码

/**************************************************************
    Problem: 4241
    User: Izumihanako
    Language: C++
    Result: Accepted
    Time:8816 ms
    Memory:5524 kb
****************************************************************/

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , M , a[100005] , arc[100005] ;
int Bsiz , Btot , bel[100005] , st[350] , ed[350] ;
struct Queries{
    int L , R , id ;
    bool operator < ( const Queries &A ) const {
        return ( bel[L] < bel[A.L] ) ||
            ( bel[L] == bel[A.L] && R < A.R ) ;
    }
}Q[100005] ;

int uninum ;
struct uniqueData{
    int num , id ;
    bool operator < ( const uniqueData &A ) const {
        return num < A.num ;
    }
}uni[100005] ;

void Unique(){
    for( int i = 1 ; i <= N ; i ++ )
        uni[i].num = a[i] , uni[i].id = i ;
    sort( uni + 1 , uni + N + 1 ) ;
    for( int i = 1 ; i <= N ; i ++ ){
        if( uni[i].num != uni[i-1].num ){
            uninum ++ ;
            arc[uninum] = uni[i].num ;
        }
        a[ uni[i].id ] = uninum ;
    }
}

void setBlo(){
    Bsiz = sqrt( N ) ;
    Btot = N / Bsiz ;
    if( N % Bsiz ) Btot ++ ;
    for( int i = 1 ; i <= Btot ; i ++ ){
        st[i] = ed[i-1] + 1 ;
        ed[i] = min( st[i] + Bsiz - 1 , N ) ;
        for( int j = st[i] ; j <= ed[i] ; j ++ )
            bel[j] = i ;
    }
}

long long ans[100005] , now ;
int cnt[100005] , cnt2[100005] ;
void solve(){
    sort( Q + 1 , Q + M + 1 ) ;
    for( int i = 1 , rg , lasB = 0 ; i <= M ; i ++ ){
        long long tmp = 0 ;
        int L = Q[i].L , R = Q[i].R ;
        if( bel[L] == bel[R] ){
            for( int j = L ; j <= R ; j ++ ){
                cnt2[ a[j] ] ++ ;
                if( tmp < 1LL * cnt2[ a[j] ] * arc[ a[j] ] )
                    tmp = 1LL * cnt2[ a[j] ] * arc[ a[j] ] ;
                //tmp = max( tmp , 1LL * cnt2[ a[j] ] * arc[ a[j] ] ) ;
            }
            ans[ Q[i].id ] = tmp ;
            for( int j = L ; j <= R ; j ++ )
                cnt2[ a[j] ] -- ;
        } else {
            if( lasB != bel[L] ){
                memset( cnt , 0 , ( uninum + 1 ) * sizeof( int ) ) ;
                lasB = bel[L] , now = 0 , rg = ed[ bel[L] ] ;
            }
            while( rg < R ){
                rg ++ , cnt[ a[rg] ] ++ ;
                if( now < 1LL * cnt[ a[rg] ] * arc[ a[rg] ] )
                    now = 1LL * cnt[ a[rg] ] * arc[ a[rg] ] ;
                //now = max( now , 1LL * cnt[ a[rg] ] * arc[ a[rg] ] ) ;
            }
            tmp = now ;
            for( int j = ed[ bel[L] ] ; j >= L ; j -- ){
                cnt[ a[j] ] ++ ;
                if( tmp < 1LL * cnt[ a[j] ] * arc[ a[j] ] )
                    tmp = 1LL * cnt[ a[j] ] * arc[ a[j] ] ;
                //tmp = max( tmp , 1LL * cnt[ a[j] ] * arc[ a[j] ] ) ;
            }
            ans[ Q[i].id ] = tmp ;
            for( int j = ed[ bel[L] ] ; j >= L ; j -- )
                cnt[ a[j] ] -- ;
        }
    }
    for( int i = 1 ; i <= M ; i ++ )
        printf( "%lld\n" , ans[i] ) ;
}

inline int read_(){
    int rt = 0 ;
    char ch = getchar() ;
    while( ch < '0' || ch > '9' ) ch = getchar() ;
    while( ch >='0' && ch <='9' ) rt = rt*10 + ch - '0' , ch = getchar() ;
    return rt ;
}

int main(){
    register int i ;
    scanf( "%d%d" , &N , &M ) ;
    for( i = 1 ; i <= N ; i ++ )
        a[i] = read_() ;
    for( i = 1 ; i <= M ; i ++ )
        Q[i].L = read_() , Q[i].R = read_() , Q[i].id = i ;
    setBlo() ;
    Unique() ;
    solve() ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值