• [NOI2016] 优秀的拆分


    题目

    点这里看题目。

    分析

    95 pts 的大暴力,枚举 AA 这样的串并且在起点、终点处记录数量。

    话说 95pts 给 (O(n^2)) 相当足了!

    目测,我们只能优化统计的过程。注意到对于 AA 串,设 (|A|=l),那么该串最多只会穿过两个相距 (l) 的点。我们可以枚举一组点,两两相邻 (l),那么就可以统计穿过相邻点的 AA 串。

    具体来说,假如我们枚举 (a,b),满足 (b=a+l)。那么当我们截取出一组合法的 AA 串、得到了子串 (S') 的时候,就一定会有 (operatorname{LCS}(S'[:a], S'[:b])+operatorname{LCP}(S'[a:], S'[b:])=l+1)

    因此我们可以找出原串上两个前缀、后缀的 (operatorname{LCS})(operatorname{LCP}),并考虑此时合法的 AA

    exce1.png

    可以发现,经过了 (a,b) 的任意两个 A 的连接点均落在橙线之间,因此此时会被贡献到的起点、终点都必然是一段区间,且可以使用差分实现 (O(1)) 修改。

    需要注意的是,为了避免计算跨越其他点的 AA,我们需要限制 (operatorname{LCS})(operatorname{LCP}) 的长度均不超过 (l)

    时间复杂度取决于求 (operatorname{LCS})(operatorname{LCP}) 的时间效率,不过 (O(nlog_2^2n))(O(nlog_2n)) 都是可以通过的。

    小结:

    1. 这道题中处理 AA 的方法确实比较巧妙,利用到的是复制的形式带来的相同结构和重复出现的性质。
    2. 其实考场上就写 95pts 应该也是完全足够的。

    代码

    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
    #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
    
    typedef long long LL;
    
    const int mod = 104857601, BASE = 29;
    const int MAXN = 1e5 + 5;
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0; char s = getchar(); int f = 1;
    	while( s < '0' || '9' < s ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
    	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
    	x *= f;
    }
    
    template<typename _T>
    void write( _T x )
    {
    	if( x < 0 ) putchar( '-' ), x = -x;
    	if( 9 < x ) write( x / 10 );
    	putchar( x % 10 + '0' );
    }
    
    template<typename _T>
    _T MIN( const _T a, const _T b )
    {
    	return a < b ? a : b;
    }
    
    template<typename _T>
    _T MAX( const _T a, const _T b )
    {
    	return a > b ? a : b;
    }
    
    int pref[MAXN], pw[MAXN];
    
    int edCnt[MAXN], bgCnt[MAXN];
    
    char S[MAXN];
    int N;
    
    inline int Mul( int x, int v ) { return 1ll * x * v % mod; }
    inline int Sub( int x, int v ) { return ( x -= v ) < 0 ? x + mod : x; }
    inline int Add( int x, int v ) { return ( x += v ) >= mod ? x - mod : x; }
    
    int Get( const int l, const int r )
    {
    	return Sub( pref[r], Mul( pref[l - 1], pw[r - l + 1] ) );
    }
    
    int LCP( const int a, const int b )
    {
    	if( S[a] ^ S[b] ) return 0;
    	int l = 1, r = MIN( a, b ), mid;
    	while( l < r )
    	{
    		mid = l + r + 1 >> 1;
    		if( Get( a - mid + 1, a ) == Get( b - mid + 1, b ) ) l = mid;
    		else r = mid - 1;
    	}
    	return l;
    }
    
    int LCS( const int a, const int b )
    {
    	if( S[a] ^ S[b] ) return 0;
    	int l = 1, r = MIN( N - a + 1, N - b + 1 ), mid;
    	while( l < r )
    	{
    		mid = l + r + 1 >> 1;
    		if( Get( a, a + mid - 1 ) == Get( b, b + mid - 1 ) ) l = mid;
    		else r = mid - 1;
    	}
    	return l;
    }
    
    void Calc( const int L )
    {
    	for( int i = L, j = L << 1 ; j <= N ; i += L, j += L )
    	{
    		int lcs = LCS( i, j ), lcp = LCP( i, j );
    		lcs = MIN( lcs, L ), lcp = MIN( lcp, L );
    		if( lcs + lcp > L )
    		{	
    			bgCnt[i - lcp + 1] ++, bgCnt[i + lcs - L + 1] --;
    			edCnt[j - lcp + L] ++, edCnt[j + lcs - 1 + 1] --;
    		}
    	}
    }
    
    int main()
    {
    //	freopen( "excellent.in", "r", stdin );
    //	freopen( "excellent.out", "w", stdout );
    	int T;
    	read( T );
    	pw[0] = 1;
    	for( int i = 1 ; i <= 3e4 ; i ++ )
    		pw[i] = Mul( pw[i - 1], BASE );
    	while( T -- )
    	{
    		scanf( "%s", S + 1 );
    		N = strlen( S + 1 ), pref[0] = 0;
    		rep( i, 1, N )
    			pref[i] = Add( Mul( pref[i - 1], BASE ), S[i] - 'a' ),
    			edCnt[i] = bgCnt[i] = 0;
    		rep( i, 1, N >> 1 ) Calc( i );
    		LL ans = 0;
    		rep( i, 1, N ) bgCnt[i] += bgCnt[i - 1], edCnt[i] += edCnt[i - 1];
    		rep( i, 1, N - 1 ) ans += 1ll * edCnt[i] * bgCnt[i + 1];
    		write( ans ), putchar( '
    ' );
    	}
    	return 0;
    }
    
  • 相关阅读:
    Ubuntu系统
    demo日常报错
    python 实现两个多维数组去重处理
    奔跑检测
    安装Win11如何绕过TPM2.0的安全限制?
    Nginx的Rewrite
    gvim 配置
    Tokyo Cabinet和Tokyo Tyrant及PHP扩展包的安装
    fm rf 删除 恢复
    提高页面loadtime的几个方法
  • 原文地址:https://www.cnblogs.com/crashed/p/14782796.html
Copyright © 2020-2023  润新知