• [CF700E]Cool Slogans


    题目

    点这里看题目。

    分析

    由于这个问题与子串相关,那么我们就先把后缀自动机给建出来。

    题目条件非常特殊——“出现至少两次”。而 fail 树上一个状态的祖先状态,根据定义,至少会在当前状态中出现一次。我们便可以知道,答案所对应的状态在 fail 树上一定呈祖孙关系

    我们可以用倍增法,求出状态 (S) 的祖先上第一个在 (S) 中出现两次以上的状态:先倍增找到最后一个只出现一次的串,它的父亲即是我们所求。我们称这个状态为 (pre(S))

    按照这个关系我们就可以设计 DP :

    (f(u)):以(u)作为末状态的字符串序列的最大长度。

    转移显然:

    [f(u)=1+max_{pre(v)=u}f(v) ]

    问题转化为了,如何判断祖先状态有没有出现多次。

    我们 使用一个暴力的方法——先用线段树合并得到每个点上的 (end-pos) 集合,再进行检查。

    对于状态 (S) ,它有祖先状态 (S') 。由于 (S) 在原串出现一次, (S') 就必然会跟着出现,我们就可以只针对某一个特殊的出现位置检查——比如, (S) 的第一个出现位置(fir(S))

    接着, (S')(S) 中出现两次以上等价于:

    [exists pin end-pos(S'),pin[fir(S)-len(S)+len(S'),fir(S)) ]

    即存在 (S') 的出现位置,满足它不属于 (end-pos(S)) ,但包含在了从 (fir(S)) 开始的 (S) 串里面。这个便是在 (end-pos) 上的区间查询问题,可以根据已有线段树信息解决。

    综合以上方法,求 (pre) 总时间 (O(nlog_2^2n)) , DP 时间 (O(n)) 。总时间 (O(nlog_2^2n))

    代码

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    
    const int MAXN = 4e5 + 5, MAXLOG = 20, MAXS = MAXN * 50;
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0;char s = getchar();int f = 1;
    	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
    	while( s >= '0' && 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 ) + 1; }
    	if( 9 < x ){ write( x / 10 ); }
    	putchar( x % 10 + '0' );
    }
    
    template<typename _T>
    _T MAX( const _T a, const _T b )
    {
    	return a > b ? a : b;
    }
    
    template<typename _T>
    _T MIN( const _T a, const _T b )
    {
    	return a < b ? a : b;
    }
    
    struct edge
    {
    	int to, nxt;
    }Graph[MAXN << 1];
    
    int f[MAXN][MAXLOG];
    bool s[MAXS]; int lch[MAXS], rch[MAXS];
    int dp[MAXN], seq[MAXN], ID;
    int ch[MAXN][26], fa[MAXN], mx[MAXN], head[MAXN], rot[MAXN], fir[MAXN];
    int N, lg2, rt, lst, tot, cnt, siz;
    char S[MAXN];
    
    void upt( const int x ) { s[x] = s[lch[x]] | s[rch[x]]; }
    void copy( int a, int b ) { fa[a] = fa[b], mx[a] = mx[b], memcpy( ch[a], ch[b], sizeof ch[b] ); }
    
    void addEdge( const int from, const int to )
    {
    	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
    	head[from] = cnt;
    }
    
    void expand( const char c )
    {
    	int x = c - 'a', p = lst, cur = ++ tot;
    	mx[cur] = mx[lst] + 1, lst = cur;
    	while( p && ! ch[p][x] ) ch[p][x] = cur, p = fa[p];
    	if( ! p ) { fa[cur] = rt; return ; }
    	int q = ch[p][x];
    	if( mx[q] == mx[p] + 1 ) { fa[cur] = q; return ; }
    	int nq = ++ tot; copy( nq, q );
    	mx[nq] = mx[p] + 1, fa[q] = fa[cur] = nq;
    	while( p && ch[p][x] == q ) ch[p][x] = nq, p = fa[p];
    }
    
    void update( int &u, const int l, const int r, const int pos )
    {
    	if( ! u ) u = ++ siz;
    	if( l == r ) { s[u] = true; return ; }
    	int mid = l + r >> 1;
    	if( pos <= mid ) update( lch[u], l, mid, pos );
    	else update( rch[u], mid + 1, r, pos );	
    	upt( u );
    }
    
    bool query( const int u, const int l, const int r, const int segL, const int segR )
    {
    	if( ! u ) return false;
    	if( segL <= l && r <= segR ) return s[u];
    	int mid = l + r >> 1, ret = false;
    	if( segL <= mid ) ret |= query( lch[u], l, mid, segL, segR );
    	if( mid < segR ) ret |= query( rch[u], mid + 1, r, segL, segR );
    	return ret;
    }
    
    int merg( int u, int v )
    {
    	if( ! u || ! v ) return u + v;
    	int cur = ++ siz;
    	lch[cur] = merg( lch[u], lch[v] );
    	rch[cur] = merg( rch[u], rch[v] );
    	s[cur] = s[u] | s[v];
    	return cur;
    }
    
    void DFS( const int u )
    {
    	seq[++ ID] = u;
    	for( int i = head[u], v ; i ; i = Graph[i].nxt )
    	{
    		DFS( v = Graph[i].to );
    		rot[u] = merg( rot[u], rot[v] );
    		fir[u] = MIN( fir[u], fir[v] );
    	}
    }
    
    int main()
    {
    	rt = lst = ++ tot;
    	memset( fir, 0x3f, sizeof fir );
    	read( N ), scanf( "%s", S + 1 );
    	for( int i = 1 ; i <= N ; i ++ ) 
    		expand( S[i] ), 
    		update( rot[lst], 1, N, i ), fir[lst] = i;
    	for( int i = 2 ; i <= tot ; i ++ ) addEdge( fa[i], i );
    	DFS( rt );
    	lg2 = log2( tot );
    	for( int i = 1 ; i <= tot ; i ++ ) f[i][0] = fa[i];
    	for( int j = 1 ; j <= lg2 ; j ++ )
    		for( int i = 1 ; i <= tot ; i ++ )
    			f[i][j] = f[f[i][j - 1]][j - 1];
    	for( int i = tot, u, p ; i ; i -- )
    	{
    		u = p = seq[i];
    		for( int k = lg2 ; ~ k ; k -- )
    			if( f[p][k] > rt && ! query( rot[f[p][k]], 1, N, fir[u] - mx[u] + mx[f[p][k]], fir[u] - 1 ) )
    				p = f[p][k];
    		p = fa[p];
    		dp[p] = MAX( dp[p], dp[u] + 1 );
    	}
    	write( dp[rt] ), putchar( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    1055. [HAOI2008]玩具取名【区间DP】
    BZOJ2435:[NOI2011]道路修建 (差分)
    1084. [SCOI2005]最大子矩阵【网格DP】
    1060. [ZJOI2007]时态同步【树形DP】
    1050. [HAOI2006]旅行【并查集+枚举】
    2463. [中山市选2009]谁能赢呢?【博弈论】
    luogu P1195 口袋的天空
    luogu P1162 填涂颜色
    luogu P1223 排队接水
    luogu P1331 海战
  • 原文地址:https://www.cnblogs.com/crashed/p/12995366.html
Copyright © 2020-2023  润新知