2 条题解

  • 0
    @ 2026-2-6 21:32:15

    $$\Large\texttt{My Blog}$$


    Description

    题目链接:Luogu 3193

    阿申准备报名参加 GT 考试,准考证号为 nn 位数 X1,X2,,XnX_1,X_2,\dots,X_n,他不望准考证号上出现不吉利的数字。他的不吉利数学 A1,A2,,AmA_1,A_2,\dots,A_mmm 位,不出现是指 X1,X2,,XnX_1,X_2,\dots,X_n 中没有恰好一段等于 A1,A2,,AmA_1,A_2,\dots,A_m。注意 A1A_1X1X_1 可以为 00

    数据范围:1n1091\le n\le 10^91m201\le m\le 201k10001\le k\le 10000Xi,Ai90\le X_i,A_i\le 9


    Solution

    我们定义 DP\text{DP} 状态 fi,jf_{i,j} 表示考虑到第 ii 个数,匹配到了 XX 中的第 jj 个字符时的方案数。显然 i,ji,j 的范围是 0in0\le i\le n0j<m0\le j<m

    转移方程为:

    fi,j=k=09fi1,pf_{i,j}=\sum_{k=0}^{9} f_{i-1,p}

    其中的 pp 不一定是 00 或者 j1j-1,因为加入字符 kk 后,有如下三种情况:

    1. 匹配到了 XX 中的下一个字符。
    2. 失配,无法匹配任何字符。
    3. 重新匹配到了 XX 的一个前缀。

    这个式子看似无法优化了,我们换一种方式写出转移方程:

    fi,j=k=0m1fi1,k×gk,jf_{i,j}=\sum_{k=0}^{m-1} f_{i-1,k}\times g_{k,j}

    其中的 gk,jg_{k,j} 表示一个匹配了长度为 kk 长度的串,有多少种加数字的方法,使得匹配长度变成 jj

    由于我们知道原串,那么 gi,jg_{i,j} 是固定的,我们可以预处理出这个数组。我们可以使用 KMP\text{KMP} 算法,求出 next\text{next} 数组后,枚举匹配长度 kk 和字符 chch,暴力计算能匹配到多长的前缀。

    这样一来,我们得到了一个 O(nm2)O(nm^2) 的算法。

    再次观察这个 DP\text{DP} 式子,可以轻松发现这个式子和矩阵乘法的式子非常相似,那么我们用矩阵快速幂优化 DP\text{DP} 转移即可,求出 gg 矩阵的 nn 次幂。

    时间复杂度O(m3logn)O(m^3\log n)


    Code

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    
    const int N=21;
    int n,m,mod,nxt[N];
    char s[N];
    
    void upd(int &x,int y) {
    	(x+=y)>=mod&&(x-=mod);
    }
    struct Matrix {
    	int n,A[N][N];
    	Matrix(int _n=0) {n=_n,memset(A,0,sizeof(A));}
    	void operator ~ () {
    		for(int i=0;i<n;++i) A[i][i]=1;
    	}
    	Matrix operator * (const Matrix &b) const {
    		Matrix ret(n);
    		for(int i=0;i<n;++i) for(int j=0;j<n;++j) for(int k=0;k<n;++k) {
    			upd(ret.A[i][k],1LL*A[i][j]*b.A[j][k]%mod);
    		}
    		return ret;
    	}
    	Matrix operator ^ (const long long &b) const {
    		Matrix ret(n),x=*this; ~ret;
    		for(long long p=b;p;p>>=1,x=x*x) if(p&1) ret=ret*x;
    		return ret;
    	}
    };
    
    Matrix kmp() {
    	nxt[1]=0;
    	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;
    	}
    	Matrix a(m);
    	for(int i=0;i<m;++i) {
    		for(char ch='0';ch<='9';++ch) {
    			int j=i;
    			while(j&&s[j+1]!=ch) j=nxt[j];
    			if(s[j+1]==ch) ++j;
    			++a.A[i][j];
    		}
    	}
    	return a;
    }
    int main() {
    	scanf("%d%d%d%s",&n,&m,&mod,s+1);
    	Matrix a=kmp();
    	a=a^n;
    	int ans=0;
    	for(int i=0;i<m;++i) upd(ans,a.A[0][i]);
    	printf("%d\n",ans);
    	return 0;
    }
    
    • 0
      @ 2025-10-8 17:01:57
      • 1

      信息

      ID
      2662
      时间
      1000ms
      内存
      256MiB
      难度
      6
      标签
      递交数
      17
      已通过
      12
      上传者