• 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 }
  • 相关阅读:
    鸡啄米vc++2010系列46(Ribbon界面开发:使用更多控件并为控件添加消息处理函数)
    鸡啄米vc++2010系列45(Ribbon界面开发:为Ribbon Bar添加控件)
    鸡啄米vc++2010系列44(Ribbon界面开发:创建Ribbon样式的应用程序框架)
    鸡啄米vc++2010系列43(MFC常用类:定时器Timer)
    鸡啄米vc++2010系列42(MFC常用类:CTime类和CTimeSpan类)
    鸡啄米vc++2010系列41(MFC常用类:CString类)
    pcl库的配置
    鸡啄米vc++2010系列40(文档、视图和框架:分割窗口)
    鸡啄米vc++2010系列39(文档、视图和框架:各对象之间的关系)
    c 函数传入数组。
  • 原文地址:https://www.cnblogs.com/refun/p/8710926.html
Copyright © 2020-2023  润新知