hdu 5318 The Goddess Of The Moon (矩阵快速幂)

题意:

题目很长,但也只有后面有用

给你n(<=50)个串,每个串长度<=10(其实也是不超过1e9的正整数)。如果a串的后缀和b串的前缀相等,并且长度>=2,则b串可以连在a串后面(注意,不用合并a,b串相同的位置)。

每个串的个数都是无穷个,现在让你选m(<=1e9)个串,问你有多少个不同的串(不是有多少个不同长度的串= =)。

11  111

111  11

上面两个串并不相同,因此算两个。


分析:

首先建立一个递推关系

串1:123456   串2:456789  串3:5612

如果此刻递推到第i位,则 dp[i][1]=dp[i-1][2]+dp[i-1][3]

即 dp[i][j]=∑dp[i-1][与j形成链接关系的串的编号]

由于i的范围可到1e9,这时候需要矩阵快速幂了



如样例

5 50

121 123 213 132 321

建立关系矩阵


G[i][j]==1表示串i可以接在串j后面。G[3][1]=1,串3可以接在串1后面

因此,dp[i][j]=∑dp[i-1][k]*G[j][k]

最终结果为dp[m][x](x从0到n)的和。


特别注意:一定要记得去重,这个被坑了无数次


#include<vector>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<string>
#include<cstdio>
#include<iostream>
#define INF 0x3f3f3f3f
#define FOR(i,f_start,f_end) for(int i=f_start;i<=f_end;++i)
#define MT(x,i) memset(x,i,sizeof(x))
using namespace std;
typedef long long LL;
const int mod = 1000000007;
const int MAX = 55;
int n,m,G[55][55],S[55];
char s[55][15];
struct Matrix{
    LL mat[55][55];
} a,b;
int sz;
void init()
{
    sort(S,S+n);
    n = unique(S, S + n) - S;
    sz = n;     
    ///以上去重
    
    for(int i=0;i<n;i++)///将输入的S数字转成char[]的s数组
        sprintf(s[i],"%d",S[i]);
    
    ///以下求G矩阵,暴力判断每对串的前缀后缀即可
    MT(G,0);
    for(int i=0; i<n; i++)
        for(int j=0; j<n; j++)
        {
            int li=strlen(s[i]),lj=strlen(s[j]),flag=0;
            int l=min(li,lj);
            for(int k=2; k<=l; k++){
                int f=1;
                for(int x=li-k,y=0; y<k; x++,y++)
                    if(s[i][x]!=s[j][y]){f=0;break;}
                if(f){flag=1;break;}
            }
            if(flag)G[j][i]=1;///j可以接在i后面
        }
}

///快速幂模板
Matrix mul(Matrix a,Matrix b)
{
    Matrix ret;
    for(int i=0; i<sz; i++)
        for(int j=0; j<sz; j++)
        {
            ret.mat[i][j]=0;
            for(int k=0; k<sz; k++)
                ret.mat[i][j] = (ret.mat[i][j] + (a.mat[i][k]*b.mat[k][j])%mod)%mod;
        }
    return ret;
}
Matrix pow_M(Matrix aa,LL nn)
{
    Matrix ret;
    memset(ret.mat,0,sizeof(ret.mat));
    for(int i=0; i<sz; i++)ret.mat[i][i]=1;
    Matrix temp=aa;
    while(nn)
    {
        if(nn&1)ret=mul(ret,temp);
        temp=mul(temp,temp);
        nn>>=1;
    }
    return ret;
}
void show(Matrix a)
{
    printf("=======show========\n");
    for(int i=0; i<sz; i++)
    {
        for(int j=0; j<sz; j++)
            printf("%d ",a.mat[i][j]);
        printf("\n");
    }
}
///快速幂模板

int main()
{
    int T;
    for(scanf("%d",&T); T--;)
    {
        MT(a.mat,0); MT(b.mat,0);
        scanf("%d%d",&n,&m);
        for(int i=0; i<n; i++)scanf("%d",&S[i]);
        if(n==0||m==0) {printf("0\n");continue;} ///特判
        
        init();
        for(int i=0; i<n; i++) a.mat[i][0] = 1;///当m为1时,每个串都可以
        //show(a);
        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
                if(G[i][j]) b.mat[i][j] = 1;///b数组就是G数组,转成模板可用的类型
        //show(b);
        
        Matrix temp = pow_M(b,m-1);///矩阵快速幂
        temp = mul(temp,a);///
        LL ans = 0;
        for(int i=0; i<n; i++)
            ans = (ans+temp.mat[i][0])%mod; /// 累加dp[m][i]
        printf("%I64d\n",ans);
    }
    return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值