说在前面
并没有什么想说的,但是要保持格式=w=
题目
BZOJ5336传送门
洛谷P4590传送门
看题可戳传送门
解法
这道题和BZOJ3864十分相似
不同之处就是不能出现连续的「NOI」,对于这个限制,我们再在dp数组加一维,表示当前已经有「NOI」的前0/1/2个字母,分类讨论转移即可
下面是代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
const int P = 1e9 + 7 ;
int N , K , mp[127] , trans[1<<15][3] , popcnt[1<<15] ;
int dp[2][1<<15][3] ;
char s[20] ;
void preWork(){
int tmp[20] , d[20] ; tmp[0] = d[0] = 0 ;
for( int se = 0 , lim = ( 1 << K ) ; se < lim ; se ++ ){
if( se ) popcnt[se] = popcnt[ se^(se&-se) ] + 1 ;
for( int j = 1 ; j <= K ; j ++ )
tmp[j] = tmp[j-1] + ( bool )( se & ( 1<<( j - 1 ) ) ) ;
for( int i = 0 ; i < 3 ; i ++ ){
memset( d , 0 , sizeof( d ) ) ;
for( int j = 1 ; j <= K ; j ++ ){
d[j] = max( d[j-1] , tmp[j] ) ;
if( s[j] == i ) d[j] = max( d[j] , tmp[j-1] + 1 ) ;
} for( int j = 0 ; j < K ; j ++ )
if( d[j] != d[j+1] ) trans[se][i] |= ( 1 << j ) ;
}
}
}
void sadd( int &a , int b ){
a += b ;
if( a >= P ) a -= P ;
}
int ans[20] ;
void solve(){
int (*now)[3] = dp[0] , (*pre)[3] = dp[1] , siz = sizeof( dp[0] ) ;
now[0][0] = 1 ;
for( int i = 1 ; i <= N ; i ++ ){
swap( now , pre ) , memset( now , 0 , siz ) ;
for( int se = 0 , lim = ( 1 << K ) ; se < lim ; se ++ ){
// N
sadd( now[ trans[se][0] ][1] , ( 1LL * pre[se][0] + pre[se][1] + pre[se][2] )%P ) ;
// O
sadd( now[ trans[se][1] ][0] , ( pre[se][0] + pre[se][2] )%P ) ;
sadd( now[ trans[se][1] ][2] , pre[se][1] ) ;
// I
sadd( now[ trans[se][2] ][0] , ( pre[se][0] + pre[se][1] )%P ) ;
}
} for( int se = 0 , lim = ( 1 << K ) ; se < lim ; se ++ )
sadd( ans[ popcnt[se] ] , ( 1LL * now[se][0] + now[se][1] + now[se][2] )%P ) ;
for( int i = 0 ; i <= K ; i ++ )
printf( "%d\n" , ans[i] ) ;
}
int main(){
mp['N'] = 0 , mp['O'] = 1 , mp['I'] = 2 ;
scanf( "%d%d%s" , &N , &K , s + 1 ) ;
for( int i = 1 ; i <= K ; i ++ ) s[i] = mp[ s[i] ] ;
preWork() ; solve() ;
}