[KMP+矩阵快速幂加速]BZOJ 1009——[HNOI2008]GT考试

题目梗概

给出一个不吉利数字 A1A2...Am(0<=Ai<=9)M

求有多少个n位数满足不包含不吉利数字。

解题思路

读完题目便能想到一个显然的数位DP, F[i][j] 表示长度为i,有长度为j的后缀与不吉利数字匹配。

考虑如何转移。

发现所有失配到j的状态都可以转移,所以要提前用KMP构造,假设我们构造出矩阵T。

但是发现还是超时。

接下来我们考虑这么修正答案:
[f[i][0],f[i][1],f[i][2]...f[i][m1]]=[f[i1][0],f[i1][1],f[i1][2]...f[i1][m1]]T

这个显然可以用矩阵快速幂优化。

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=25;
int n,m,tt,nxt[maxn],sum;
char s[maxn];
struct jz{
    int n,m,x[maxn][maxn];
}T,ans;
void make_nxt(){
    for (int i=2,j=0;i<=m;i++){
        while (j&&s[j+1]!=s[i]) j=nxt[j];
        if (s[j+1]==s[i]) j++;
        nxt[i]=j;
    }
}
jz mul(jz a,jz b){
    jz c;memset(c.x,0,sizeof(c.x));
    c.n=a.n;c.m=b.m;
    for (int i=0;i<c.n;i++)
    for (int j=0;j<c.m;j++)
    for (int k=0;k<a.m;k++)
    c.x[i][j]=(c.x[i][j]+a.x[i][k]*b.x[k][j])%tt;
    return c;
}
jz qsm(jz x,int b){
    jz c;memset(c.x,0,sizeof(c.x));
    c.n=x.n;c.m=x.m;
    for (int i=0;i<=c.n;i++) c.x[i][i]=1;
    while(b>0){
        if (b%2==1) c=mul(c,x);
        x=mul(x,x);
        b>>=1;
    }
    return c;
}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    scanf("%d%d%d",&n,&m,&tt);
    scanf("%s",s+1);
    make_nxt();
    T.n=T.m=m;
    for (int i=0;i<m;i++)
    for (char j='0';j<='9';j++){
        int k=i;
        while(k&&s[k+1]!=j) k=nxt[k];
        if (s[k+1]==j) k++;
        if (k<m) T.x[i][k]=(T.x[i][k]+1)%tt;
    }
    ans.n=1,ans.m=m;ans.x[0][0]=1;ans=mul(ans,qsm(T,n));
    for (int i=0;i<m;i++) sum=(sum+ans.x[0][i])%tt;
    printf("%d\n",sum);
    return  0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值