版权声明:本文为博主原创文章,未经博主允许不得转载。 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次幂:
- 表示从i到j走1步的走法。
- 做一次乘法:,也就是以k为一次中转,表示从i到j走2步的走法。
- 那么就表示从i到j走k步的走法。
对于本题,设数组表示当前匹配到个字符,添加一个字符变成匹配为个字符的方案数。这里的和就可以看成状态。
利用数组考虑每个状态走下一步之后的情况,建成一张有向图。
然后对数组做n次的快速幂,表示添加n个字符之后的状态。累加就是答案。
#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);
}