BZOJ1009 [HNOI2008]GT考试 矩阵

去博客园看该题解

题目  

【bzoj1009】[HNOI2008]GT考试

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

4 3 100
111

Sample Output

81

题解

  设dp[i][j]表示总共到做第i位,匹配到第j位的ans,那么对于dp[i][j]到dp[i+1][j'],一定是有固定的转移试的,不会因为运算的值改变而改变。那么想到了什么?矩阵乘法!!设p[i][j]表示匹配到第i位之后,再匹配j,所能得到的新的匹配长度,则可以构建矩阵:对于每一个p[i][j],矩阵的第p[i][j]行第i列加1。

  至于匹配p[i][j],两种方法-->暴力匹配或者kmp都可以。

  矩阵匹配长度范围是:0~m-1。注意,匹配不可到达m,因为如果匹配到了m,那么就是一个不吉利的串出现了!所以只能匹配到第m-1个。那么在kmp的时候,对于匹配0个的转移要特殊处理。

还是举一个例子吧——

  对于111的转移:

  要转移到匹配0位,那么如果之前匹配了0或者1或者2位,只要再接下去一个非1的数字即可转移到,那么:

  dp[i+1][0]= 9* dp[i][0] + 9* dp[i][1] + 9* dp[i][2]

  同理:

  dp[i+1][1]= 1* dp[i][0] + 0* dp[i][1] + 0* dp[i][2]

  dp[i+1][2]= 0* dp[i][0] + 1* dp[i][1] + 0* dp[i][2]

  再来一个稍微复杂一些的:1213

      dp[i][0] dp[i][1] dp[i][2] dp[i][3]

dp[i+1][0]=  9*   8*   9*   9*

dp[i+1][1]=  1*   1*   0*   1*

dp[i+1][2]=  0*   1*   0*   0*

dp[i+1][3]=  0*   0*   1*   0*

所以,根据dp方程就可以构建矩阵了,然后跑矩阵快速幂,就可以拿到满分了!

代码

#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cmath>
using namespace std;
const int M=20+5;
int n,m,mod;
struct Mat{
    int v[M][M];
    void set(int x){
        memset(v,0,sizeof v);
        if (x==1)
            for (int i=0;i<m;i++)
                v[i][i]=1;
    }
    Mat operator * (Mat x){
        Mat ans;
        ans.set(0);
        for (int i=0;i<m;i++)
            for (int j=0;j<m;j++)
                for (int k=0;k<m;k++){
                    ans.v[i][j]+=v[i][k]*x.v[k][j];
                    if (ans.v[i][j]>=mod)
                        ans.v[i][j]%=mod;
                }
        return ans;
    }
}M1,My,Mans;
Mat Pow(int y){
    if (y==0)
        return M1;
    Mat x=Pow(y/2);
    x=x*x;
    if (y&1)
        x=x*My;
    return x;
}
int p[M][12],next[M];
char ch[M];
int main(){
    scanf("%d%d%d%s",&n,&m,&mod,&ch);
    int k=0;
    memset(next,0,sizeof next);
    for (int i=1;i<m;i++){
        while (k>0&&ch[i]!=ch[k])
            k=next[k-1];
        if (ch[i]==ch[k])
            k++;
        next[i]=k;
    }
    for (int j=0;j<=9;j++)
        if (ch[0]==j+'0')
            p[0][j]=1;
        else
            p[0][j]=0;
    for (int i=1;i<m;i++)
        for (int j=0;j<=9;j++){
            char chj=j+'0';
            int k=i;
            while (k>0&&ch[k]!=chj)
                k=next[k-1];
            if (ch[k]==chj)
                k++;
            p[i][j]=k;
        }
    M1.set(1);
    My.set(0);
    for (int i=0;i<m;i++)
        for (int j=0;j<=9;j++)
            My.v[i][p[i][j]]++;
    Mans=Pow(n);
    int ans=0;
    for (int i=0;i<m;i++)
        ans=(ans+Mans.v[0][i])%mod;
    printf("%d",ans);
    return 0;
}

 

posted @ 2017-07-30 15:15  zzd233  阅读(304)  评论(0编辑  收藏  举报