zoukankan      html  css  js  c++  java
  • bzoj1009 [HNOI2008]GT考试

    1009: [HNOI2008]GT考试

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 4517  Solved: 2799
    [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位的数。 N<=10^9,M<=20,K<=1000

    Output

      阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.

    Sample Input

    4 3 100
    111

    Sample Output

    81
    分析:以前做过一道类似的题:传送门,但是本题的n特别大,不能存到状态里面,怎么办? 
       如果当前匹配到不吉利数字的号码的第j位,枚举第j+1位的数字,能够转移到的地方是固定的. 构造一个转移矩阵,令a[i][j]表示不吉利数字的第i位跳到第j位有多少种方案. 这个很容易利用kmp算法求出来.那么f[i][j] = f[i-1][0] * a[0][j] + f[i - 1][1] * a[1][j] +......
    这是一个矩阵相乘的表达式,可以用矩阵快速幂来优化.
       通过矩阵快速幂,可以不用记录n,最后的答案就是矩阵第一行的和.因为第m列是不符合要求的,所以实际上只需要记录矩阵的0~m-1列即可.
       这道题的转移方式和那道题有很大的区别. 因为要用到矩阵快速幂不记录n,必须知道f[i][j]从哪些状态转移而来,转移过来的方案数是多少,那么就必须要求出a数组. 相反的,如果直接递推转移,那么很容易就能知道f[i][j]能够转移到哪些状态,直接从f[i][j] 转移到f[i + 1][k]即可.由此可以看出“转移到”和“转移来”有很大的区别,记录的东西也会有所不同.
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int n,m,mod,nextt[30],ans;
    char s[30];
    
    struct node
    {
        int x[30][30];
        void clear()
        {
            memset(x,0,sizeof(x));
        }
    }a,b,c;
    
    void operator *= (node &p,node &q)
    {
        c.clear();
        for (int i = 0; i < m; i++)
            for (int j = 0; j < m; j++)
                for (int k = 0; k < m; k++)
                    c.x[i][j] = (c.x[i][j] + p.x[i][k] * q.x[k][j] % mod) % mod;
        p = c;
    }
    
    void kmp()
    {
        int j = 0;
        for (int i = 2; i <= m; i++)
        {
            while (j && s[j + 1] != s[i])
                j = nextt[j];
            if (s[j + 1] == s[i])
                j++;
            nextt[i] = j;
        }
        int k;
        b.clear();
        for (int i = 0; i < m; i++)
        {
            for (char j = '0'; j <= '9'; j++)
            {
                k = i;
                while (k && s[k + 1] != j)
                    k = nextt[k];
                if (s[k + 1] == j)
                    k++;
                b.x[i][k]++;
            }
        }
    }
    
    void qpow(int p)
    {
        a.clear();
        for (int i = 0; i < m; i++)
            a.x[i][i] = 1;
        while (p)
        {
            if (p & 1)
                a *= b;
            b *= b;
            p >>= 1;
        }
    }
    
    int main()
    {
        scanf("%d%d%d",&n,&m,&mod);
        scanf("%s",s + 1);
        kmp();
        qpow(n);
        for (int i = 0; i < m; i++)
            ans = (ans + a.x[0][i]) % mod;
        printf("%d
    ",ans);
    
        return 0;
    }
        
  • 相关阅读:
    Big Event in HDU
    ACM STEPS——Chapter Two——Section One
    黑马day15 文件上传&amp;apche的工具包
    阿里云部署Docker(8)----安装和使用redmine
    【React Native开发】React Native控件之ProgressBarAndroid进度条解说(12)
    python抓取新浪微博评论并分析
    使用Xcode-debug模式和release模式
    hdu 5417 Victor and Machine
    《Head First 设计模式》学习笔记——适配器模式 + 外观模式
    设计模式(二)
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8461466.html
Copyright © 2011-2022 走看看