【BZOJ】【1009】 【HNOI2008】GT考试
DP/KMP/矩阵乘法
好神的题啊……跪了跪了
$n\leq 10^9$是什么鬼……我们还是先不要考虑这个鬼畜的玩意了>_>
用类似数位DP的思路,我们可以想到一个DP方程:$f[i][j]$表示前 i 位数字,它的最后 j 位与不吉利串匹配的方案数,显然有$ans=\sum_{i=0}^x f[n][i]$
然后就是转移的问题了= =那么依旧按照数位DP的想法(其实是硬扯到那的吧……怎么理解都可以,重点是明白转移方程)可以想到:从 i 转移到 i+1,有10种方案,其中一种会使得匹配长度+1,即$f[i+1][j+1]+=f[i][j]$那么其他的方案呢?并不都会使得匹配长度归0,想到这你大概已经想到了,对!就是KMP!字符串匹配时的fail指针!那么转移的方式就是这三种了= =:1.匹配长度归0;2.匹配长度+1;3.匹配长度变为fail[j]+1。
那么转移方式已经确定啦~接下来就该考虑一下$n\leq 10^9$这个蛋疼的范围了……
很明显这个范围O(n)是不可能的了= =必须要加速!那么我们来研究一下这个转移:我们可以把$f_i$看作一个向量:$$\begin{bmatrix} f_0 f_1 \cdots f_{m-1} \end{bmatrix}(即0\leq j\leq m-1) $$ 从$f_i$转移到$f_{i+1}$,其实是可以用一个矩阵来表示的: $$ \begin{bmatrix} f_0& f_1 &\cdots &f_{m-1} \end{bmatrix}_{i} * \begin{bmatrix} a_{0,0}& a_{0,1}& \cdots &a_{0,m-1} \\ a_{1,0}& a_{1,1}& \cdots &a_{1,m-1} \\ \ddots& \ddots& \vdots &\ddots \\ a_{m-1,0}& a_{m-1,1}& \cdots &a_{m-1,m-1} \end{bmatrix} = \begin{bmatrix} f_0 &f_1 &\cdots &f_{m-1} \end{bmatrix}_{i+1} $$
嗯左边的表示$f[i][j]$,右边就是$f[i+1][j]$了;那么a矩阵是什么玩意呢?这是一个转移矩阵:$a[i][j]$表示从匹配了 i 个字符转移到匹配了 j 个字符有多少种方案!很明显当$j>0$的时候f[i][j]只可能是0或1,这里可能需要仔细理解一下,反正$f[i+1]$跟$f[i]$是线性相关的!所以我们可以直接利用矩阵乘法加速!
嗯这道题我写的时候由于本题有【匹配长度为0】这个状态……然后KMP很久没写过了……果断跪啊,这题貌似是需要在原来的KMP上稍微改动一下,由于我KMP理解的不是很好所以蛋疼了很久……最后是看了Hzwer的写法才过的
1 /************************************************************** 2 Problem: 1009 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:56 ms 7 Memory:812 kb 8 ****************************************************************/ 9 10 //BZOJ 1009 11 #include<cstdio> 12 #define rep(i,n) for(int i=0;i<n;++i) 13 #define F(i,j,n) for(int i=j;i<=n;++i) 14 const int N=25,INF=~0u>>2; 15 /******************tamplate*********************/ 16 int n,m,P; 17 struct Matrix{ 18 int x[N][N]; 19 int* operator [] (int a) {return x[a];} 20 Matrix(int a=0){ 21 rep(i,N) rep(j,N) 22 if (i==j) x[i][j]=a; 23 else x[i][j]=0; 24 } 25 }; 26 Matrix operator*(Matrix a,Matrix b){ 27 Matrix c; 28 rep(i,m) rep(j,m) rep(k,m) 29 c[i][j]=(c[i][j]+a[i][k]*b[k][j])%P; 30 return c; 31 } 32 Matrix Pow(Matrix a,int b){ 33 Matrix r(1); 34 for(;b;b>>=1,a=a*a) if(b&1) r=r*a; 35 return r; 36 } 37 /*******************Matrix**********************/ 38 char s[100]; 39 Matrix f,a; 40 int next[100]; 41 void KMP(){ 42 int j=0; 43 F(i,2,m){ 44 while (j && s[i]!=s[j+1]) j=next[j]; 45 if (s[j+1]==s[i]) j++; 46 next[i]=j; 47 } 48 rep(i,m) 49 rep(j,10){ 50 int x=i; 51 while(x && s[x+1]-'0'!=j) x=next[x]; 52 if (j==s[x+1]-'0') a[i][x+1]++; 53 else a[i][0]++; 54 } 55 } 56 int main(){ 57 scanf("%d%d%d",&n,&m,&P); 58 scanf("%s",s+1); 59 KMP(); 60 f=Pow(a,n); 61 int ans=0; 62 rep(i,m) ans+=f[0][i],ans%=P; 63 printf("%d\n",ans); 64 return 0; 65 }
1009: [HNOI2008]GT考试
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 2015 Solved: 1233
[Submit][Status][Discuss]
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位的数。 100%数据N<=10^9,M<=20,K<=1000 40%数据N<=1000 10%数据N<=6
Output
阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.
Sample Input
111