Loading

背单词—题解

比较套路的一个\(fail\)树的题

首先有一个比较显然的结论。

权为负数的字符串必定不用选

很容易证明

选择一个字符串只会让我们之后选择的余地变小,所以不选一个字符串之后的选择空间更大,一个字符串如果权值为负数,那么选他既会让我们的答案变小,又会让之后的选择更少,所以不选肯定比选更优

先想\(f[i]\)为选择第\(i\)个字符串之后的最大收益,答案就是\(max_{i=1}^{n} f[i]\)
然后看到这个题,就不难想到一个纸张方程\(f[i]=max(f[k])+w[i]\)
也就是说,我们只需要知道一个点的子串都是谁就行了。

首先在\(fail\)树上,我们是可以比较轻松的判断子串的。
由于这个点的\(fail\)必定是他的后缀,所以我们只需要遍历他的前缀,对每一个前缀不断跳\(fail\)就可以。
其次,我们再来思考一下\(fail\)树的性质。
一个点的所有子树必定都是包含自己的字符串,也就是这个节点的字符串必定是他所有子树的子串。
然后就可以想到一个刷表转移\(f[y]=max(f[y],f[x]+w[x])\)
都想到他在树上,并且要刷表转移了,那么不难想到用\(dfs\)序+线段树优化。
到达一个节点之后,我们单点查询他前缀的每一个点,就是\(f[x]\)
然后直接区间修改就行了。

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <string>
#include <iostream>
#include <assert.h>
#define mp make_pair
#define R register int
#define int long long
#define printf Ruusupuu = printf

int Ruusupuu ;

using namespace std ;
typedef long long L ;
typedef long double D ;
typedef unsigned long long G ;
typedef pair< int , int > PI ;
const int N = 2e4 + 10 ;
const int M = 3e5 + 10 ;

inline int read(){
	int w = 0 ; bool fg = 0 ; char ch = getchar() ;
	while( ch < '0' || ch > '9' ) fg |= ( ch == '-' ) , ch = getchar() ;
	while( ch >= '0' && ch <= '9' ) w = ( w << 1 ) + ( w << 3 ) + ( ch ^ '0' ) , ch = getchar() ;
	return fg ? -w : w ; 
}

int T , n , lnt , w [N] , x ; //ohters
string s [N] ;// strings 
int tr [M][27] , fail [M] , fl [N] , tnt , res ; //AC
int cnt , fr [M] , to [M] , net [M] , head [M] ; // fail tree
int lz [M] , rz [M] , knt , pos [M] ; //dfs order
int l [M << 2] , r [M << 2] , data [M << 2] , ly [M << 2] , lx , rx , dt , ps ; // seg tree

inline void clear(){
	dt = ps = lx = rx = dt = ps = knt = tnt = res = cnt = 0 ;
	memset( lz , 0 , sizeof( lz ) ) ;
	memset( rz , 0 , sizeof( rz ) ) ;
	memset( pos , 0 , sizeof( pos ) ) ;
	memset( tr , 0 , sizeof( tr ) ) ;
	memset( fail , 0 , sizeof( fail ) ) ;
	memset( fl , 0 , sizeof( fl ) ) ;
	memset( head , -1 , sizeof( head ) ) ;
	memset( fr , 0 , sizeof( fr ) ) ;
	memset( to , 0 , sizeof( to ) ) ;
	memset( net , 0 , sizeof( net ) ) ;
	memset( l , 0 , sizeof( l ) ) ;
	memset( r , 0 , sizeof( r ) ) ;
	memset( ly , 0 , sizeof( ly ) ) ;
	memset( data , 0 , sizeof( data ) ) ;
}


#define addE( f , t ) { fr [++ cnt] = f , to [cnt] = t , net [cnt] = head [f] , head [f] = cnt ; } 
#define ud( x )  data [x] = data [x << 1] > data [x << 1 | 1] ? data [x << 1] : data [x << 1 | 1] ; 

inline void sp( int x ){
	if( !ly [x] ) return ;
	int tt = ly [x] ; ly [x] = 0 ;
	data [x << 1] = data [x << 1] > tt ? data [x << 1] : tt ;
	ly [x << 1] = ly [x << 1] > tt ? ly [x << 1] : tt ;
	data [x << 1 | 1] = data [x << 1 | 1] > tt ? data [x << 1 | 1] : tt ;
	ly [x << 1 | 1] = ly [x << 1 | 1] > tt ? ly [x << 1 | 1] : tt ;
}

inline void buildT( int x , int ls , int rs ){
	l [x] = ls , r [x] = rs ;
	if( ls == rs ) return ; 
	int mid = ( ls + rs ) >> 1 ;
	buildT( x << 1 , ls , mid ) , buildT( x << 1 | 1 , mid + 1 , rs ) ;  
}

inline int ask( int x ){
	if( l [x] == r [x] ) return data [x] ;
	sp ( x ) ;
	int mid = ( l [x] + r [x] ) >> 1 ;
	if( ps <= mid ) return ask( x << 1 ) ;
	else return ask( x << 1 | 1 ) ; 
}

inline void upd( int x ){
	if( l [x] >= lx && r [x] <= rx ) { data [x] = data [x] > dt ? data [x] : dt ; res = max( res , data [x] ) , ly [x] = ly [x] > dt ? ly [x] : dt ; return ; }
	sp( x ) ; int mid = ( l [x] + r [x] ) >> 1 ;
	if( lx <= mid ) upd( x << 1 ) ;
	if( rx > mid ) upd( x << 1 | 1 ) ;
	ud( x ) ;
}

inline void ins( string s , int ix ){
	int len = s.size() , x = 0 ;
	for( R i = 0 ; i < len ; i ++ ){
		int ch = s [i] - 'a' ;
		if( !tr [x][ch] ) tr [x][ch] = ++ tnt ;
			x = tr [x][ch] ;
	} fl [ix] = x ;
}

void build(){
	queue< int > q ;   
	for( R i = 0 ; i < 26 ; i ++ ) if( tr [0][i] ) q.push( tr [0][i] ) ;
	
	while( !q.empty() ){
		int x = q.front() ; q.pop() ;
		for( R i = 0 ; i < 26 ; i ++ )
			if( tr [x][i] ) fail [tr [x][i]] = tr [fail [x]][i] , q.push( tr [x][i] ) ;
			else tr [x][i] = tr [fail [x]][i] ;
	}
	for( R i = 1 ; i <= tnt ; i ++ ) addE( fail [i] , i ) ;  
}

inline void reins( int ix ){
	string f = s [ix] ;
	int x = 0 , len = f.size() , kax = 0 ;
	for( R i = 0 ; i < len ; i ++ )
		x = tr [x][f [i] - 'a'] , ps = lz [x] , kax = max( kax , ask( 1 ) ) ;
	int kes = kax + w [ix] ; 
 	lx = lz [x] , rx = rz [x] , dt = kes ;
	upd( 1 ) ; 
	res = max( res , ask( 1 ) ) ;
}

void dfs( int x ){
	lz [x] = ++ knt , pos [knt] = x ;
	for( R i = head [x] ; ~i ; i = net [i] )
		dfs( to [i] ) ;	
	rz [x] = knt ;
}

void sc(){
	n = read() , lnt = 1 ;
	for( R i = 1 ; i <= n ; i ++ ){
		cin >> s [lnt] , x = read() ;
		if( x > 0 ) ins ( s [lnt] , lnt ) , w [lnt ++] = x ;
	} n = -- lnt , build() ;
}

void work(){
	dfs( 0 ) , buildT( 1 , 1 , knt ) ; 	
	for( R i = 1 ; i <= n ; i ++ ) reins( i ) ;
	printf( "%lld\n" , res ) ;
}

signed main(){
	T = read() ;	
	while( T -- ){
		clear() ;
		sc() ;
		work() ;
	}	
	return 0 ;
} 
posted @ 2021-07-02 14:11  Soresen  阅读(69)  评论(0)    收藏  举报