萌新第一篇题解 希望能有帮助 (本来打算交某谷的 但是写完发现题解已经满了qwq)
首先看到n和m范围巨大的差距就要想到矩阵快速幂
转移方式为直接在自动机上dp
和其他题解一样 这里设g[i][j]表示最长匹配到第i个数字 连接上数字j后的状态
~~蒟蒻语文不好 看不明白可以参考其他题解~~
蒟蒻看到各位大佬求g用的都是kmp 这里提出一种不一样的求法
我们发现g[i][j]的定义和AC自动机的tr[i][j]定义非常像
实际上 g[i][j] 就是一个只有一条链的AC自动机
于是我们可以用建AC自动机的方法求g
当然我们不需要真的建一颗tire出来
直接令第i个数字为第i个状态 令g[i][num[i+1]]=i+1
(num[1-m]为数字串) 然后直接建自动机即可(个人感觉比kmp方便很多)
具体代码:
for(int i=0;i<m;i++)g[i][num[i+1]]=i+1;
for(int i=1;i<=m;i++)for(int j=0;j<10;j++)
if(g[i][j])fail[g[i][j]]=g[fail[i]][j];
else g[i][j]=g[fail[i]][j];
之后矩阵加速dp即可
关于的矩阵乘法顺序 这里和大多数题解不太一样 看个人喜好
在该顺序下转移矩阵的i,j项表示i状态对j状态贡献的系数
复杂度:O(矩阵快速幂标准复杂度) ~~等我会了markdown可能会来补~~
最后贴上常数极大 码风混乱的代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
#define clr(x) memset(x,0,sizeof(x))
inline int read(){//快读模板
int x=0;char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=10*x+c-'0',c=getchar();
return x;
}
inline int getc(){
char c=getchar();
while(c<'0'||c>'9')c=getchar();
return c-'0';
}
const int maxn=30;
int num[maxn],fail[maxn],g[maxn][10],n,m,p;
struct mix{//蒟蒻的矩阵模板
int n,m,val[maxn][maxn];
mix(){clr(val);}
mix(int a,int b){n=a,m=b;clr(val);}
mix(int a){n=m=a;clr(val);for(int i=1;i<=a;i++)val[i][i]=1;}//单位矩阵
int* operator[](int x){return val[x];}
mix operator*(mix x){
mix res(n,x.m);
for(int i=1;i<=n;i++)for(int j=1;j<=x.m;j++)
for(int k=1;k<=m;k++)res[i][j]=(res[i][j]+val[i][k]*x[k][j]%p)%p;
return res;
}
};
mix qpow(mix a,int b){//快速幂
mix res(a.n);
for(;b;b>>=1){
if(b&1)res=res*a;
a=a*a;
}
return res;
}
signed main(){
n=read(),m=read(),p=read();
mix a(1,m),b(m,m);a[1][1]=1;//起始状态(其实是0)
for(int i=1;i<=m;i++)num[i]=getc();
for(int i=0;i<m;i++)g[i][num[i+1]]=i+1;//建立自动机g[i][j]
for(int i=1;i<=m;i++)for(int j=0;j<10;j++)
if(g[i][j])fail[g[i][j]]=g[fail[i]][j];
else g[i][j]=g[fail[i]][j];
for(int i=0;i<m;i++)for(int j=0;j<10;j++)if(g[i][j]!=m/*不能转移到m状态*/)b[i+1][g[i][j]+1]++;//由于这里矩阵下标从1开始 所以状态要+1
int ans=0;a=a*qpow(b,n);//转移
for(int i=1;i<=m;i++)ans+=a[1][i],ans%=p;//统计答案
return printf("%lld",ans),0;//输出
}