[BZOJ1009][HNOI2008]GT考试
题目描述
阿申准备报名参加 GT 考试,准考证号为 NNN 位数 X1,X2…Xn(0≤Xi≤9)X_1,X_2…X_n(0\le X_i\le9)X1,X2…Xn(0≤Xi≤9) ,他不希望准考证号上出现不吉利的数字。 他的不吉利数学 A1,A2…Am(0≤Ai≤9)A_1,A_2…A_m(0\le A_i\le 9)A1,A2…Am(0≤Ai≤9) 有 MMM 位,不出现是指 X1,X2…XnX_1,X_2…X_nX1,X2…Xn 中没有恰好一段等于 A1,A2…AmA_1,A_2…A_mA1,A2…Am , A1A_1A1 和 X1X_1X1 可以为 000
输入输出格式
输入格式:第一行输入N,M,K.接下来一行输入M位的数。
输出格式:阿申想知道不出现不吉利数字的号码有多少种,输出模 KKK 取余的结果。
输入输出样例
4 3 100 111
81
说明
N≤109,M≤20,K≤1000N\leq10^9,M\leq20,K\leq1000N≤109,M≤20,K≤1000
一脸不可做...
其实貌似不难我就调了两个小时。。
设f[i, j]表示填到了第i位,与不吉利数字匹配到第j位的方案总数。
设g[i, j]表示在第i位后添加一个数字,转移到j的方案数;
g数组可以用kmp求出,当然暴力也行因为长度太小...其实我暴力调了好长时间换成kmp过了...
我也不知道为何玄学事情总在我身上发生...
显然f[i][j] = Σ(i:1~m-1)f[i-1][k] * g[k][j]。
意思就是f[i][j]是从i-1位加一个数转移过来的。
n范围1e9,考虑矩阵加速...
我们的矩阵是个这个样子:
g[0][0] g[1][0] g[2][0] ... g[m-2][0] g[m-1][0] f[i][0] f[i+1][0]
g[0][1] g[1][1] g[2][1] ... g[m-2][1] g[m-1][0] f[i][1] f[i+1][1]
... * ... = ...
... ... ...
g[m-1][1]... f[i][m-1] f[i+1][m-1]
然后快乐地转移...就做完了...
吐槽一句:打矩阵好累啊
#include <iostream> #include <cstdio> #include <cstring> using namespace std; using namespace std; inline int read(){ int res=0;char ch=getchar(); while(!isdigit(ch))ch=getchar(); while(isdigit(ch)){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();} return res; } int n, m, mod; int g[25][25], nxt[25]; int str[25]; int ans; struct Mat{ int a[25][25]; Mat(){memset(a, 0, sizeof a);} void init(){for(int i=0;i<25;i++)a[i][i]=1;} friend Mat operator * (Mat x, Mat y) { Mat z; for (int k = 0 ; k < 25 ; k ++) { for (int i = 0 ; i < 25 ; i ++) { for (int j = 0 ; j < 25 ; j ++) { z.a[i][j] = (z.a[i][j] + x.a[i][k] * y.a[k][j]) % mod; } } } return z; } friend Mat operator ^ (Mat x, int y) { Mat z;z.init(); while(y) { if (y&1) z = z * x; x = x * x; y >>= 1; } return z; } }A, res, B; int main() { n = read(), m = read(), mod = read(); for (int i = 1 ; i <= m ; i ++) scanf("%1d", &str[i]); int p = 0; for(int i = 2 ; i <= m ; i ++) { while(p and str[p+1] != str[i])p = nxt[p]; if(str[p+1]==str[i])p++; nxt[i]=p; } for (int i = 0 ; i < m ; i ++) { for (int j = 0 ; j <= 9 ; j ++) { int k = i; while(k and str[k+1] != j) k=nxt[k]; if (str[k+1] == j) k++; if(k != m) g[i][k] ++; if (g[i][k] >= mod) g[i][k] -= mod; } } for (int i = 0 ; i <= m - 1 ; i ++) { for (int j = 0 ; j <= m - 1 ; j ++) { A.a[i][j] = g[j][i]; } } B.a[0][0] = 1; res = A ^ n; res = res * B; for (int i = 0 ; i <= m - 1 ; i ++) ans = (ans + res.a[i][0]) % mod; cout<<ans<<endl; return 0; }