背单词—题解
比较套路的一个\(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 ;
}
$The \ light \ has \ betrayed \ me$

浙公网安备 33010602011771号