Description
阿申准备报名参加GT考试,准考证号为N位数X1X2…Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。
他的不吉利数学A1A2…Am(0<=Ai<=9)有M位,不出现是指X1X2…Xn中没有恰好一段等于A1A2…Am. A1和X1可以为
0
Input
第一行输入N,M,K.接下来一行输入M位的数。 N<=10^9,M<=20,K<=1000
Output
阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.
题解:
Orz,这道题也太神了…其实看了一眼就知道是DP了,奈何数据范围太大了,就没往递推想了…- -!
设dp[i][j]表示前i个字符匹配了前j个不吉利数字的方案数,也就是 [ i − j + 1 , i ] [i-j+1, i] [i−j+1,i]都是不吉利的数字,那么答案就是 ∑ i = 0 m − 1 d p [ n ] [ i ] \sum_{i=0}^{m-1}dp[n][i] ∑i=0m−1dp[n][i]
考虑怎么转移,由于前i个字符匹配了前j个不吉利数字,那么设当前字符集为S,不吉利数字集为T,那么
S
[
i
−
j
+
1
]
S[i-j+1]
S[i−j+1] ~
S
[
i
]
S[i]
S[i]与
T
[
1
]
T[1]
T[1] ~
T
[
j
]
T[j]
T[j]是完全相同的,然后我们利用KMP得到的next数组快速求到当前位可以匹配到T的第几位,然后转移:
d
p
[
i
]
[
j
]
=
∑
k
=
0
m
−
1
d
p
[
i
−
1
]
[
k
]
∗
a
[
k
]
[
j
]
dp[i][j] = \sum_{k=0}^{m-1}dp[i-1][k]*a[k][j]
dp[i][j]=k=0∑m−1dp[i−1][k]∗a[k][j]
其中a[i][j]表示从第i位转移到第j位可能的方案数
考虑到n的大小,而且dp方程的转移又类似矩阵乘法,于是利用矩阵快速幂求出答案即可
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
#define int long long
#define pii pair<int,int>
#define mp(a,mat) make_pair(a,mat)
const int MAXN = 25;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
char s[MAXN];
int ans[MAXN][MAXN],a[MAXN][MAXN],mat[MAXN][MAXN],nxt[MAXN];
int n,m,mod,res;
inline void KMP(){
for(int i=2,j=0;i<=m;i++){
while(j && s[i]!=s[j+1]) j=nxt[j];
if(s[i]==s[j+1]) j++;
nxt[i]=j;
}
for(int i=0;i<m;i++){
for(int j=0;j<=9;j++){
int tmp=i;
while(tmp && s[tmp+1]-'0'!=j) tmp=nxt[tmp];
if(s[tmp+1]-'0'==j) tmp++;
a[i][tmp]++;
}
}
}
inline void mul(int res[MAXN][MAXN],int opt){
memset(mat,0,sizeof(mat));
for(int i=0;i<opt;i++)
for(int j=0;j<m;j++)
for(int k=0;k<m;k++)
mat[i][j]=(mat[i][j]+res[i][k]*a[k][j])%mod;
if(opt==1) memcpy(ans,mat,sizeof(mat));
else memcpy(a,mat,sizeof(mat));
}
inline void qmi(){
ans[0][0]=1;
while(n){
if(n&1) mul(ans,1LL);
n>>=1;
mul(a,m);
}
for(int i=0;i<m;i++) res=(res+ans[0][i])%mod;
cout<<res<<'\n';
}
signed main(){
#ifndef ONLINE_JUDGE
freopen("C:\\Users\\Administrator\\Desktop\\in.txt","r",stdin);
#endif // ONLINE_JUDGE
cin>>n>>m>>mod>>(s+1);
KMP(); qmi();
return 0;
}