[HNOI2008]GT考试

[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;
}
posted @ 2020-07-22 21:07  HH_Halo  阅读(145)  评论(0编辑  收藏  举报