说在前面
这真是me遇见过的,近年来最简单的NOI题…
题目
BZOJ4199传送门
UOJ131传送门
洛谷P2178传送门
题面就不粘了,反正哪儿都可以看
解法
根据题目,如果两个位置向后延伸r长度 的字符串都相同,那么就会对0到r的答案产生贡献
也就是要统计各个长度时,子串相等的情况
不难想到后缀自动姬(因为后缀自动姬的right集合是从后向前延伸,并且parent树上的节点代表的就是相同子串的集合)
于是对反串建出后缀自动姬,并考虑在parent树上搞事
对于第一小问,每个parent节点对它的长度len新增的贡献,就是各个子树的叶子节点个数乘积。一开始把主链上的cnt设为1,然后逆拓扑序走一遍就可以统计,最后累加上来(ways[len]+=ways[len+1])
对于第二小问,在每个节点维护最大值,次大值,最小值,次小值,然后逆拓扑序走的时候顺便更新答案就好。me写的比较丑,特判一大坨…
下面是自带大常数的代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
const int inf = 0x3f3f3f3f ;
char ss[300005] ;
int N , id_c , a[300005] , ttt ;
long long ways[300005] , ans[300005] ;
struct Node{
int len , cnt ;
int max1 , max2 , min1 , min2 ;
Node *ch[26] , *par ;
}*root , *las , w[600005] , *tw = w ;
void newNode( Node *&nd , int len ){
nd = ++ tw ; ++id_c ;
nd->max1 = nd->max2 = -inf ;
nd->min1 = nd->min2 = inf ;
nd->len = len ;
}
void Insert( int id ){
Node *nd , *tmp = las ;
newNode( nd , tmp->len + 1 ) ; nd->cnt = 1 ;
if( a[ttt] > 0 ) nd->max1 = a[ttt] ;
else nd->min1 = a[ttt] ;
ttt -- ;
// printf( "%p\n" , nd ) ;
for( ; tmp && !tmp->ch[id] ; tmp = tmp->par )
tmp->ch[id] = nd ;
if( !tmp ) nd->par = root ;
else{
Node *B = tmp->ch[id] ;
if( B->len == tmp->len + 1 ) nd->par = B ;
else{
Node *nB ; newNode( nB , tmp->len + 1 ) ;
nB->par = B->par ;
B->par = nd->par = nB ;
memcpy( nB->ch , B->ch , sizeof( nB->ch ) ) ;
while( tmp && tmp->ch[id] == B )
tmp->ch[id] = nB , tmp = tmp->par ;
}
} las = nd ;
}
int sa[600005] , buc[300005] ;
void Rsort(){
for( int i = 1 ; i <= id_c ; i ++ ) buc[ w[i].len ] ++ ;
for( int i = 1 ; i <= N ; i ++ ) buc[i] += buc[i-1] ;
for( int i = id_c ; i ; i -- ) sa[ buc[w[i].len]-- ] = i ;
}
void solve(){
for( int i = id_c ; i >= 1 ; i -- ){
Node *nd = &w[ sa[i] ] , *par = nd->par ;
long long &ans = ::ans[ nd->len ] ;
if( nd->max2 != -inf )
ans = max( ans , 1LL * nd->max1 * nd->max2 ) ;
if( nd->min2 != inf )
ans = max( ans , 1LL * nd->min1 * nd->min2 ) ;
if( ans == - ( 1LL << 60 ) && nd->min1 != inf && nd->max1 != -inf )
ans = max( ans , 1LL * nd->min1 * nd->max1 ) ;
// if( nd->len == N - 1 ) printf( "spe :: %lld\n" , ans ) ;
// printf( "%p par(%p):%d %d %d %d\n" , nd , par , nd->max1 , nd->max2 , nd->min1 , nd->min2 ) ;
if( !par ) continue ;
ways[ par->len ] += 1LL * par->cnt * nd->cnt ;
par->cnt += nd->cnt ;
if( par->max1 <= nd->max1 ){
par->max2 = max( par->max1 , nd->max2 ) ;
par->max1 = nd->max1 ;
} else if( nd->max1 > par->max2 )
par->max2 = nd->max1 ;
if( nd->max2 > par->max2 )
par->max2 = nd->max2 ;
if( par->min1 >= nd->min1 ){
par->min2 = min( par->min1 , nd->min2 ) ;
par->min1 = nd->min1 ;
} else if( nd->min1 < par->min2 )
par->min2 = nd->min1 ;
if( nd->min2 < par->min2 )
par->min2 = nd->min2 ;
}
for( int i = N ; i >= 0 ; i -- ){
ways[i] += ways[i+1] ;
ans[i] = max( ans[i] , ans[i+1] ) ;
}
for( int i = 0 ; i < N ; i ++ )
printf( "%lld %lld\n" , ways[i] , ways[i] == 0 ? 0LL : ans[i] ) ;
}
int main(){
newNode( root , 0 ) ; root->par = NULL ;
las = root ;
scanf( "%d%s" , &N , ss ) ;
for( int i = 0 ; i < N ; i ++ ){
scanf( "%d" , &a[i] ) ;
} ttt = N - 1 ;
for( int i = N + 1 ; i >= 0 ; i -- )
ans[i] = - ( 1LL << 60 ) ;
for( int i = N - 1 ; i >= 0 ; i -- )
Insert( ss[i] - 'a' ) ;
Rsort() ; solve() ;
}