[HNOI2008]GT考试
题目描述
阿申准备报名参加 GT 考试,准考证号为 (N) 位数(X_{1},X_{2}…X_{n}(0≤X_{i}≤9)),他不希望准考证号上出现不吉利的数字。 他的不吉利数学(A_{1},A_{2}…A_{m}(0≤A_{i}≤9)) 有 (M) 位,不出现是指 (X_{1},X_{2}…X_{n}) 中没有恰好一段等于 (A_{1},A_{2}…A_{m}),(A_{1}) 和(X_{1}) 可以为 (0)
输入格式
第一行输入(N),(M),(K).接下来一行输入M位的数。
输出格式
阿申想知道不出现不吉利数字的号码有多少种,输出模 (K) 取余的结果。
输入输出样例
输入 #1
4 3 100
111
输出 #1
81
说明/提示
(N≤10^{9},M≤20,K≤1000)
思路分析
本来就是想好好想练一下(KMP),根据某谷标签找到了这道题,看题面那么简短,以为并不是很难(虽然是紫的),结果做着做着就傻了
这题很综合,(DP)+矩阵快速幂+(KMP),我原地螺旋升天sto orz
状态转移:
- 设(dp[i][j])表示长度为(i)的字符串,最后(j)个可以匹配模式串前(j)位的情况数,答案就是(sum_{i=0}^{m-1}dp[n][i])
- However,理论是有了,如何实现?
- 关键在于对模式串的不同匹配情况,用一个(g[k][j])对于一个匹配到长度为 (j)的串,转移到 (k) 的串的方案
- 此时转移方程(dp[i][j] = sum_{k=0}^{m-1}dp[i-1][k]*g[k][j]),你看,你看,你仔细看,这个式子像什么?矩阵乘啊!(本蒟蒻永远推不出来系列)
- 因为我们最后只需要(n)这一行矩阵,所以我们把每一行(dp[i][j])抽象成只有一行,列数为(m-1)的(F[i]),最后就变成了(F[i] = F[i-1]*G),推出(F[n] = F[0]*G^{n}),这时候就可以跑矩阵快速幂了
等会儿,我不是来练(KMP)的吗???
Code
#include<bits/stdc++.h>
#define N 25
using namespace std;
inline int read(){
int x = 0,f = 1;
char ch = getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
int n,m,mod,kmp[N];
char s[N];
struct xzy{//定义矩阵
int a[N][N];
xzy(){memset(a,0,sizeof(a));}
}G,F;
xzy operator *(xzy a,xzy b){//矩阵乘法
xzy ans;
for(int i = 0;i < m;i++){
for(int j = 0;j < m;j++){
for(int k = 0;k < m;k++){
ans.a[i][j] = (ans.a[i][j]+a.a[i][k]*b.a[k][j]%mod)%mod;
}
}
}
return ans;
}
xzy quickpow(xzy g,int t){//矩阵快速幂
xzy ans;
for(int i = 0;i < m;i++)ans.a[i][i] = 1;
while(t){
if(t&1)ans = ans*g;
g = g*g;
t >>= 1;
}
return ans;
}
int main(){
n=read();m=read();mod=read();
scanf("%s",s+1);
int j=0;
for(int i=2;i<=m;++i) {//kmp求匹配情况
while(j && s[j+1]!=s[i]) j=kmp[j];
if(s[j+1]==s[i]) j++;
kmp[i]=j;
}
for(int i=0;i<m;++i) {//算出所有未完全匹配的个数(这好像才是我的本意……)
for(char ch='0';ch<='9';++ch) {//枚举所有位数
int j=i;
while(j && s[j+1]!=ch) j=kmp[j];//失配回跳
if(s[j+1]==ch) j++;//该情况成立
G.a[i][j]=(G.a[i][j]+1)%mod;//加上一种情况
}
}
F.a[0][0]=1;//F是一行m列的
G=quickpow(G,n);
F=F*G;
int ans=0;
for(int i=0;i<m;++i) {
ans=(ans+F.a[0][i])%mod;
}
printf("%d",ans);
return 0;
}