其他分享
首页 > 其他分享> > BZOJ 4310 跳蚤

BZOJ 4310 跳蚤

作者:互联网

BZOJ 4310 跳蚤

不太会做,看了题解才会的。

首先要二分子串。后缀排序后,本质不同子串个数其实就是 $ \sum_i n + 1 - sa[i] - height[i] $ ,考虑排序后的后缀,本质不同的子串个数其实就是本质不同这些后缀的前缀个数。一个后缀的贡献就是这个后缀的所有前缀,减去自己和上一个后缀的 LCP 。(感性理解一下吧QAQ)

其实二分后,这个子串是可以确定的。枚举一下后缀排序后的后缀就可以得到。

假设我们当前二分到的子串是 $ s[L,R] $ ,怎么确定是否可以分成很多份,使得每一份里最大的子串也不大于这个?

这个时候就要贪心了,从后往前划分,如果往开头添加当前这个字符,整个串都没有大于 $ s[L,R] $ 我们就添加它,否则在这里 cut 开。

这个贪心很容易证成立的,显然可以添加这个字符的话我们会尽力去添加它,因为一定不会让情况变得不优秀。

然后这个比较大小还有点毒瘤,如果要比较 $ s[l,r] $ 和 $ s[L,R] $ 大小,先拿 $ l,n $ 和 $ L , n $ 比较,这个可以 $ O(1) $

看起来不是很好写呢~ awa

而且不太明白为啥 $ k $ 很小。

记得longlong啊。。没longlong挂了两发。。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 100006
#define C 130
int k;
char ch[MAXN];
int sa[MAXN] , tp[MAXN] , rnk[MAXN] , buc[MAXN] , len , ht[MAXN];
int P[MAXN][19];
void init(  ) {
    len = strlen( ch + 1 ); int m = C;
    for( int i = 1 ; i <= len ; ++ i ) ++ buc[rnk[i] = ch[i]];
    for( int i = 1 ; i <= m ; ++ i ) buc[i] += buc[i-1];
    for( int i = len ; i >= 1 ; -- i ) sa[buc[rnk[i]] --] = i;
    for( int k = 1 , p = 0 ; p < len ; k <<= 1 ) {
        p = 0;
        for( int i = 0 ; i <= m ; ++ i ) buc[i] = 0;
        for( int i = len - k + 1 ; i <= len ; ++ i ) tp[++p] = i;
        for( int i = 1 ; i <= len ; ++ i ) if( sa[i] > k ) tp[++p] = sa[i] - k;
        for( int i = 1 ; i <= len ; ++ i ) ++ buc[rnk[i]];
        for( int i = 1 ; i <= m ; ++ i ) buc[i] += buc[ i-1 ];
        for( int i = len ; i >= 1 ; -- i ) sa[buc[rnk[tp[i]]] --] = tp[i];
        p = 1;
        swap( rnk , tp );
        rnk[sa[1]] = 1;
        for( int i = 2 ; i <= len ; ++ i )
            rnk[sa[i]] = ( tp[sa[i]] == tp[sa[i-1]] && tp[sa[i] + k] == tp[sa[i-1] + k] ) ? p : ++ p;
        m = p;
    }
    for( int i = 1 ; i <= len ; ++ i ) rnk[sa[i]] = i;
    for( int i = 1 , k = 0 ; i <= len ; ++ i ) {
        if( k ) -- k;
        while( ch[i + k] == ch[sa[rnk[i] - 1] + k] ) ++ k;
        ht[rnk[i]] = k; P[rnk[i]][0] = k;
    }
}

void initst( ){
    for( int k = 1 ; k < 19 ; ++ k )
        for( int i = 1 ; i <= len - ( 1 << k ) + 1 ; ++ i )
            P[i][k] = min( P[i][k - 1] , P[i + ( 1 << k - 1 )][k - 1] );
}
int que( int l , int r ) {
    if( l > r ) swap( l , r ); else if( l == r ) return 0x3f3f3f3f;
    ++ l;
    int L = 31 - __builtin_clz( r - l + 1 );
    return min( P[l][L] , P[r - ( 1 << L ) + 1][L] );
}

int L , R;
void getlr( long long x ) {
    for( int i = 1 ; i <= len ; ++ i ) {
        int k = len + 1 - ht[i] - sa[i];
        if( x > k ) x -= k;
        else { L = sa[i]; R = sa[i] + ht[i] + x - 1; break; }
    }
}
bool cmp( int l , int r , int L , int R ) { // return S[l,r] > S[L,R]
    int ret = 1;
    if( rnk[l] < rnk[L] ) swap( l , L ) , swap( r , R ) , ret ^= 1;
    int l1 = r - l + 1 , l2 = R - L + 1;
    if( l1 < l2 ) {
        int lp = que( rnk[l] , rnk[L] );
        if( lp >= l1 ) return ret ^ 1;
        else return ret;
    } else if( l1 == l2 ) {
        int lp = que( rnk[l] , rnk[L] );
        if( lp >= l1 ) return 0;
        else return ret;
    }
    return ret;
}
bool chk( long long x ) {
    getlr( x );
    int r = len , t = 0;
    for( int i = len ; i >= 1 ; -- i ) {
        if( ch[i] > ch[L] ) return false;
        if( cmp( i , r , L , R ) ) r = i , ++ t;
        if( t >= k ) return false;
    }
    return true;
}

int main() {
//    freopen("2.in","r",stdin);
//    freopen("ot","w",stdout);
    cin >> k;
    scanf("%s",ch+1);
    init();
    initst();
    long long tot = 0;
    for( int i = 1 ; i <= len ; ++ i ) tot += len + 1 - ht[i] - sa[i];
    long long l = 1 , r = tot;
    while( l <= r ) {
        long long m = l + r >> 1;
        if( chk ( m ) ) r = m - 1;
        else l = m + 1;
//        cout << m << endl;
    }
    getlr( l );
    for( int i = L ; i <= R ; ++ i ) putchar( ch[i] );
}

标签:4310,rnk,return,int,后缀,MAXN,sa,BZOJ,跳蚤
来源: https://www.cnblogs.com/yijan/p/bzoj4310.html