[HNOI2008]GT考试

题目

洛谷
BZOJ

做法

朴素做法:

设数组(dp[i][j])为前(i)位后缀(j)位恰好(避免特判)对应禁止字符前缀(j)

(dp[i][j]=sumlimits_{c=0}^m k[c] dp[i-1][c]),由于是恰好对应,系数(k)(KMP)随便搞搞就出来了

数位dp差不多

丢到矩阵里快速幂

My complete code

#include<bits/stdc++.h>
using namespace std;
typedef int LL;
const LL maxn=31;
LL n,m,p,ans;
LL nxt[maxn],a[maxn][maxn];
char s[maxn];
struct Mat{
	LL v[maxn][maxn];
	inline void Init(LL x=0){
		memset(v,0,sizeof(v));
		for(LL i=0;i<m;++i) v[i][i]=x;
	}
}M;
inline Mat Mul(const Mat &a,const Mat &b){
	Mat c; c.Init();
	for(LL i=0;i<m;++i)
	    for(LL j=0;j<m;++j)
	        for(LL k=0;k<m;++k)
	            c.v[i][j]=(c.v[i][j]+a.v[i][k]*b.v[k][j])%p;
	return c;
}
inline Mat Fpow(Mat base,LL b){
	Mat ret; ret.Init(1);
	while(b){
		if(b&1) ret=Mul(ret,base);
		base=Mul(base,base),b>>=1;
	}return ret;
}
int main(){
	scanf("%d%d%d %s",&n,&m,&p,s);
	
	LL len(strlen(s)),k(0);
	for(LL i=1;i<m;++i){
		while(k && s[k]!=s[i])
		    k=nxt[k-1];
		if(s[k]==s[i]) ++k;
		nxt[i]=k;
	}
	a[0][s[0]-'0']=1;
	for(LL i=1;i<m;++i)
	    for(LL ch=0;ch<=9;++ch){
	    	LL k(i);
	    	char c(ch+'0');
	    	while(k && s[k]!=c)
	    	    k=nxt[k-1];
	    	if(s[k]==c) ++k;
	    	a[i][ch]=k;
		}
	M.Init();
	for(LL i=0;i<m;++i)
	    for(LL ch=0;ch<=9;++ch)
	        ++M.v[i][a[i][ch]];
	M=Fpow(M,n);
	for(LL i=0;i<m;++i)
	    ans=(ans+M.v[0][i])%p;
	printf("%d",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/y2823774827y/p/10369849.html