hdu5318(2015多校3)--The Goddess Of The Moon(dp+矩阵优化)

题目链接:点击打开链接

题目大意:给出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) ;
    }

}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值