题目链接:点击打开链接
题目大意:给出n种串,每种串有无限多个,现在要在这n种串中选择m个链接起来,链接的规则是:如果a串的后缀(len >= 2 )是b串的前缀,那么就可以把b接到a的后面,问最终可以组成多少个不同的串
首先应该排除重复的,因为重复的不会多产生链接。然后找出对于第i种串,后面可以接哪几个串。
然后dp[i][j],当链接了i个串后,以第j个串结尾的有多少种。这样dp[i][j] = ∑dp[i-1][k] (k串后面可以接j)
因为m<=1e9,所以用矩阵优化,初始时dp[1][i]全部为1,计算矩阵temp.a[i][j],如果第i个串后面可以接j,那么temp.a[i][j] = 1,否则为0,这样dp[i]和temp矩阵相乘都会得到dp[i+1],使用矩阵快速幂计算出dp[1]*(temp.a)^(m-1)的和
#include <cstdio>
#include <cstring>
#include <stack>
#include <algorithm>
using namespace std ;
#pragma comment(linker, "/STACK:1024000000,1024000000")
#define LL __int64
const int MOD = 1e9+7 ;
struct node{
LL a[60][60] ;
};
char str[60][60] ;
LL a[60] , len[60] ;
stack<int> sta ;
int n ;
node mul(node p,node q) {
int i , j , k ;
node temp ;
for(i = 0 ; i < n ; i++) {
for(j = 0 ; j < n ; j++){
temp.a[i][j] = 0 ;
for(k = 0 ; k < n ; k++) {
temp.a[i][j] = (temp.a[i][j] + p.a[i][k]*q.a[k][j]) % MOD ;
}
}
}
return temp ;
}
node pow(node p,int k) {
node temp ;
int i , j ;
memset(temp.a,0,sizeof(temp.a)) ;
for(i = 0 ; i < n ; i++) temp.a[i][i] = 1 ;
while( k ) {
if( k%2 ) temp = mul(temp,p) ;
p = mul(p,p) ;
k /=2 ;
}
return temp ;
}
int main() {
int t , m ;
int i , j , k , l ;
node temp ;
LL ans ;
scanf("%d", &t) ;
while( t-- ) {
scanf("%d %d", &n, &m) ;
for(i = 0 ; i < n ; i++)
scanf("%I64d", &a[i]) ;
sort(a,a+n) ;
n = unique(a,a+n)-a;
while( !sta.empty() ) sta.pop() ;
for(i = 0 ; i < n ; i++) {
do{
sta.push(a[i]%10) ;
a[i] /= 10 ;
}while( a[i] ) ;
len[i] = 0 ;
while( !sta.empty() ) {
str[i][ len[i]++ ] = sta.top()+'0' ;
sta.pop() ;
}
str[i][ len[i] ] == '\0' ;
}
memset(temp.a,0,sizeof(temp.a)) ;
for(i = 0 ; i < n ; i++) {
for(j = 0 ; j < n ; j++) {
for(l = 2 ; l <= len[i] && l <= len[j] ; l++) {
for(k = 0 ; k < l ; k++)
if( str[i][ len[i]-l+k ] != str[j][k] ) break ;
if( k >= l ){
temp.a[i][j] = 1 ;
break ;
}
}
}
}
temp = pow(temp,m-1) ;
for(i = 0 , ans = 0 ; i < n ; i++){
for(j = 0 ; j < n ; j++) {
ans = (ans + temp.a[i][j]) % MOD ;
}
}
printf("%I64d\n", ans) ;
}
}