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

Sample Output

81

Solution

好题。不看题解不会写题系列。
虽然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

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 }
posted @ 2018-04-03 20:22  Refun  阅读(400)  评论(0编辑  收藏  举报