BZOJ 1009: [HNOI2008]GT考试

BZOJ 1009: [HNOI2008]GT考试

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取余的结果.

Sample Input

4 3 100
111

Sample Output

81

HINT

Source

Solution

这一题显然是数位dp的老套路了,之前做过一个ac自动机多串的dp。
这只有一个串显然是用kmp了。但是n<=10^9需要考虑优化。
观察dp方程:

\(f[i][j]=\sum_{k=0}^{m-1}f[i-1][k]*a[k][j]\)

f[i][j]表示放到第i个后缀ji为匹配串的前缀1j。a[k][j]为加一个字母由k转移到j的放置方案数。
f显然是线性转移,可以使用矩阵快速幂优化,完美的过了本题。

Code

#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,x) for(int i=head[x];i;i=e[i].next)
#define mem(a,x) memset(a,x,sizeof(a))
typedef long long LL;
typedef double DB;
using namespace std;
inline int read() {
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') f=(ch=='-')?-1:f,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+(ch-'0'),ch=getchar();return f*x;
}
const int SZ=1000010,INF=0x3f3f3f3f;
int n,m,mod,next[233];
char s[233];
void get_next() {
	int l=strlen(s);
	next[0]=next[1]=0;
	fo(i,1,l-1) {
		int j;
		for(j=next[i];j&&s[i]!=s[j];j=next[j]);
		next[i+1]=(s[i]==s[j])?j+1:0;
	}
}
struct matrix {
	int n,m,num[30][30];
	matrix(int a,int b):n(a),m(b) {memset(num,0,sizeof(num));}
};
matrix operator *(const matrix &a,const matrix &b) {
	matrix ans(a.n,b.m);
	fo(i,0,ans.n-1) fo(j,0,ans.m-1) fo(k,0,a.m-1)
		ans.num[i][j]=(ans.num[i][j]+(LL)a.num[i][k]*b.num[k][j]%mod)%mod;
	return ans;
}
matrix ksm(matrix a,int b) {
	matrix ans(a.n,a.m);
	fo(i,0,ans.n-1) ans.num[i][i]=1;
	for(;b;a=a*a,b>>=1) if(b&1) ans=ans*a;
	return ans;
}
int main() {
	freopen("1009.in","r",stdin);
	n=read(),m=read(),mod=read(),scanf("%s",s),get_next();
	matrix f(m,m);
	fo(i,0,m-1) fo(j,0,9) {
		int k;
		for(k=i;k&&s[k]!='0'+j;k=next[k]);
		if(s[k]=='0'+j) k++;
		if(k!=m) f.num[i][k]++;
	}
	matrix fn=ksm(f,n),a(1,m);
	a.num[0][0]=1;
	a=a*fn;
	int ans=0;
	fo(i,0,m-1) ans=(ans+a.num[0][i])%mod;
	printf("%d\n",ans);
	return 0;
}
posted @ 2017-12-10 11:46  _patrick  阅读(185)  评论(0编辑  收藏  举报