BZOJ1009:[HNOI2008]GT考试(AC自动机,矩乘DP)
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
111
Sample Output
81
Solution
好题。不看题解不会写题系列。
虽然AC自动机的题解本来就没几篇我还一篇都没看懂
一开始口胡的写法是对的不过因为一个小瑕疵写挂了
建议先写过BZOJ1030文本生成器再来写这个题
不然这个题解可能看不懂
而且因为我比较菜不会的东西太多所以这个题解可能很长……
考虑暴力,我们会发现这个题和我做过的BZOJ1030好像几乎一模一样……
就连DP式子都一样
只不过N太大了没法转移是么……
虽然AC自动机的题解本来就没几篇我还一篇都没看懂
一开始口胡的写法是对的不过因为一个小瑕疵写挂了
建议先写过BZOJ1030文本生成器再来写这个题
不然这个题解可能看不懂
而且因为我比较菜不会的东西太多所以这个题解可能很长……
考虑暴力,我们会发现这个题和我做过的BZOJ1030好像几乎一模一样……
就连DP式子都一样
只不过N太大了没法转移是么……
贴一段文本生成器的AC代码。
for (int i=1;i<=m;++i)
for (int j=0;j<=sz;++j)
for (int k=0;k<26;++k)
if (!End[Son[j][k]])
(f[i][Son[j][k]]+=f[i-1][j])%=MOD;
我们发现外层的m(也就是本题的N)循环的时候,里面两个循环每次进行的转移都是机械一样的。
都是根据父亲的状态来推儿子的状态。
这样的话,我们就可以用矩阵快速幂来优化了。
举个例子
4 3 100
111
这是样例。
我们将初始矩阵start定义为[1,0,0],这对应的是文本生成器一题中的初始化f[0][0]=1;
然后将x节点与x的所有儿子节点在转移矩阵a中a[x][son]+=1
这样在矩乘转移的时候我们就可以把父亲的状态推到儿子了
最后答案即为start*a^n
因为很容易发现,start是f[0][]的所有状态
start*a是f[1][]的所有状态
那么start*a^n即为f[n][]的所有状态
将start*a^n矩阵里的所有数值加起来即为所求答案。
orz感觉自己已经是一条咸鱼了
对了送组数据:
1000000000 19 9973
1010100110011000001
5753
for (int i=1;i<=m;++i)
for (int j=0;j<=sz;++j)
for (int k=0;k<26;++k)
if (!End[Son[j][k]])
(f[i][Son[j][k]]+=f[i-1][j])%=MOD;
我们发现外层的m(也就是本题的N)循环的时候,里面两个循环每次进行的转移都是机械一样的。
都是根据父亲的状态来推儿子的状态。
这样的话,我们就可以用矩阵快速幂来优化了。
举个例子
4 3 100
111
这是样例。
我们将初始矩阵start定义为[1,0,0],这对应的是文本生成器一题中的初始化f[0][0]=1;
然后将x节点与x的所有儿子节点在转移矩阵a中a[x][son]+=1
这样在矩乘转移的时候我们就可以把父亲的状态推到儿子了
最后答案即为start*a^n
因为很容易发现,start是f[0][]的所有状态
start*a是f[1][]的所有状态
那么start*a^n即为f[n][]的所有状态
将start*a^n矩阵里的所有数值加起来即为所求答案。
orz感觉自己已经是一条咸鱼了
对了送组数据:
1000000000 19 9973
1010100110011000001
5753
Code
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<queue> 5 #define N (10005) 6 using namespace std; 7 8 int Son[N][11],End[N],Fail[N]; 9 int n,m,sz,MOD; 10 char s[N]; 11 queue<int>q; 12 13 struct Matrix 14 { 15 int m[25][25]; 16 void clear(){memset(m,0,sizeof(m));}; 17 }; 18 19 Matrix operator * (Matrix a,Matrix b) 20 { 21 Matrix ans; ans.clear(); 22 for (int i=1; i<=sz+1; ++i) 23 for (int j=1; j<=sz+1; ++j) 24 for (int k=1; k<=sz+1; ++k) 25 (ans.m[i][j]+=a.m[i][k]*b.m[k][j])%=MOD; 26 return ans; 27 } 28 29 Matrix Qpow(Matrix a,int p) 30 { 31 Matrix ans; ans.clear(); 32 for (int i=1; i<=sz+1; ++i) ans.m[i][i]=1; 33 while (p) 34 { 35 if (p&1) ans=ans*a; 36 a=a*a; p>>=1; 37 } 38 return ans; 39 } 40 41 void Insert(char s[]) 42 { 43 int now=0,len=strlen(s); 44 for (int i=0; i<len; ++i) 45 { 46 int x=s[i]-'0'; 47 if (!Son[now][x]) Son[now][x]=++sz; 48 now=Son[now][x]; 49 } 50 End[now]|=1; 51 } 52 53 void Build_Fail() 54 { 55 for (int i=0; i<10; ++i) 56 if (Son[0][i]) 57 q.push(Son[0][i]); 58 while (!q.empty()) 59 { 60 int now=q.front(); 61 q.pop(); 62 for (int i=0; i<10; ++i) 63 { 64 if (!Son[now][i]) 65 { 66 Son[now][i]=Son[Fail[now]][i]; 67 continue; 68 } 69 End[Son[now][i]]|=End[Son[Fail[now]][i]]; 70 Fail[Son[now][i]]=Son[Fail[now]][i]; 71 q.push(Son[now][i]); 72 } 73 } 74 } 75 76 int main() 77 { 78 Matrix a; a.clear(); 79 Matrix start; start.clear(); 80 start.m[1][1]=1; 81 82 scanf("%d%d%d",&n,&m,&MOD); 83 scanf("%s",s),Insert(s); 84 Build_Fail(); 85 for (int i=0;i<=sz;++i) 86 for (int j=0;j<10;++j) 87 if (!End[Son[i][j]]) 88 a.m[i+1][Son[i][j]+1]++; 89 a=Qpow(a,n); 90 a=start*a; 91 int ans=0; 92 for (int i=1;i<=sz+1;++i) 93 (ans+=a.m[1][i])%=MOD; 94 printf("%d",ans); 95 }