[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;
}
原文地址:https://www.cnblogs.com/hhhhalo/p/13363151.html