【KMP+矩阵】BZOJ - 1009 - GT考试

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/monochrome00/article/details/84072605

题目链接<https://cn.vjudge.net/problem/HYSBZ-1009>


题意:

给出一串长度为m的不吉利数字,要构造出一个长度为n的数字串不包含这个不吉利的数字,问构造方案数是多少。

(m<=20,n<=1e9)


题解:

简化版的https://blog.csdn.net/monochrome00/article/details/82859506

对于一张有向图,恰好走K步(可以走重边)的方案数就是求邻接矩阵的k次幂:

  • mat[i][j]表示从i到j走1步的走法。
  • 做一次乘法:mat[i][j]=\sum(mat[i][k]*mat[k][j]),也就是以k为一次中转,表示从i到j走2步的走法。
  • 那么mat^{k}就表示从i到j走k步的走法。

对于本题,设数组mat[i][j]表示当前匹配到i个字符,添加一个字符变成匹配为j个字符的方案数。这里的ij就可以看成状态。

利用next数组考虑每个状态走下一步之后的情况,建成一张有向图。

然后对mat数组做n次的快速幂,表示添加n个字符之后的状态。累加mat[0][i]就是答案。


#include<bits/stdc++.h>
using namespace std;
const int N=50;
int n,m,mod;
char s[N];
int nex[N];
void getnex(){
    int i=0,j=-1;
    nex[i]=-1;
    while(i<m){
        if(s[i]==s[j]||j==-1) i++,j++,nex[i]=j;
        else j=nex[j];
    }
}
void mul(int a[N][N],int b[N][N]){
    int c[N][N];
    memset(c,0,sizeof(c));
    for(int i=0;i<m;i++)
        for(int j=0;j<m;j++)
            for(int k=0;k<m;k++)
                c[i][j]=(c[i][j]+a[i][k]*b[k][j])%mod;
    memcpy(a,c,sizeof(c));
}
void mpow(int a[N][N],int b){
    int c[N][N];
    for(int i=0;i<m;i++)
        for(int j=0;j<m;j++)
            c[i][j]=(i==j);
    for(int i=b;i;i>>=1,mul(a,a))
        if(i&1) mul(c,a);
    memcpy(a,c,sizeof(c));
}
int a[N][N];
int main(){
    scanf("%d%d%d",&n,&m,&mod);
    scanf("%s",s);
    getnex();
    for(int i=0;i<m;i++){
        for(int j=0;j<=9;j++){
            int tmp=i;
            while(1){
                if(j==s[tmp]-'0'||tmp==-1){
                    if(tmp==m-1) break;
                    a[i][tmp+1]++;
                    break;
                }
                tmp=nex[tmp];
            }
        }
    }
    mpow(a,n);
    int ans=0;
    for(int i=0;i<m;i++)
        ans=(ans+a[0][i])%mod;
    printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/monochrome00/article/details/84072605