题目链接:bzoj点我:-) 洛谷点我:-)
题目描述:
阿申准备报名参加GT考试,准考证号为N位数X1X2….Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。他的不吉利数学A1A2…Am(0<=Ai<=9)有M位,不出现是指X1X2…Xn中没有恰好一段等于A1A2…Am. A1和X1可以为0
输入格式:
第一行输入N,M,K.接下来一行输入M位的数。 N<=
109
,M<=20,K<=1000
输出格式:
阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.
思路:
令
f[i][j]
表示原串第
i
位匹配了不吉利数列(
则对于原串中每一个
后接数字要么等于
unlu[j+1]
,那么
k
为
要么找到前面与其匹配的位置
k
然后用
感想:
实话实说,本题没有想出来
本来想DP方程,然而短路了,看到
n
的范围被吓了一跳,不敢想了
然而是真的DP和矩乘
为避免下次短路,总结一下矩乘的一般使用情况:
1.DP方程的数据很大,要么数学题(我本题居然想了半天容斥)要么矩乘
2.DP方程的递推为线性的,即有关于
3.DP方程的状态可以在矩阵中存下来
4.不是一些奇怪的方程,例如
f[n]=f[n/2]
之类
还有就是 kmp 终于脱离了老人家的从0起步的奇怪做法,终于改为从1起步了
代码:
//miaomiao 2017.2.12
#include<cstdio>
#include<cstring>
using namespace std;
#define Set(a, v) memset(a, v, sizeof(a))
#define For(i, a, b) for(int i = (a); i <= (int)(b); i++)
#define M (20+5)
int n, rm, P, unlu[M], f[M];
struct Matrix{
int m[M][M];
Matrix(int x=0){
Set(m, 0); if(!x) return;
For(i, 0, rm) m[i][i] = 1;
}
Matrix operator *(const Matrix &rhs)const{
Matrix ret;
For(k, 0, rm-1) For(i, 0, rm-1) if(m[i][k]) For(j, 0, rm-1)
ret.m[i][j] = (ret.m[i][j]+m[i][k]*rhs.m[k][j]%P)%P;
return ret;
}
Matrix operator ^(int num){
Matrix ret(1), a = *this;
while(num){
if(num&1) ret = ret*a;
a = a*a; num >>= 1;
}
return ret;
}
}base;
int to[M][10];
inline void getfail(){
For(i, 1, rm-1){
int j = f[i];
while(j && unlu[i+1]!=unlu[j+1]) j=f[j];
f[i+1] = unlu[i+1]==unlu[j+1]? j+1: 0;
}
For(i, 0, rm-1) For(j, 0, 9){
to[i][j] = unlu[i+1]==j? i+1: to[f[i]][j];
base.m[i][to[i][j]]++;
}
}
int main(){
scanf("%d%d%d", &n, &rm, &P);
For(i, 1, rm) scanf("%1d", &unlu[i]);
getfail();
Matrix a; a.m[0][0] = 1;
a = a*(base^n);
int sum = 0;
For(i, 0, rm-1) sum += a.m[0][i];
printf("%d\n", sum%P);
return 0;
}