[BZOJ1009][HNOI2008]GT考试

题目描述

阿申准备报名参加 GT 考试,准考证号为 NNN 位数 X1,X2…Xn(0≤Xi≤9)X_1,X_2…X_n(0\le X_i\le9)X1,X2Xn(0Xi9) ,他不希望准考证号上出现不吉利的数字。 他的不吉利数学 A1,A2…Am(0≤Ai≤9)A_1,A_2…A_m(0\le A_i\le 9)A1,A2Am(0Ai9) 有 MMM 位,不出现是指 X1,X2…XnX_1,X_2…X_nX1,X2Xn 中没有恰好一段等于 A1,A2…AmA_1,A_2…A_mA1​,A2​…Am​ , A1A_1A1​ 和 X1X_1X1​ 可以为 000

输入输出格式

输入格式:

第一行输入N,M,K.接下来一行输入M位的数。

输出格式:

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

输入输出样例

输入样例#1: 
4 3 100
111
输出样例#1: 
81

说明

N≤109,M≤20,K≤1000N\leq10^9,M\leq20,K\leq1000N109,M20,K1000

 

 


 

一脸不可做...

其实貌似不难我就调了两个小时。。

设f[i, j]表示填到了第i位,与不吉利数字匹配到第j位的方案总数。

设g[i, j]表示在第i位后添加一个数字,转移到j的方案数;

g数组可以用kmp求出,当然暴力也行因为长度太小...其实我暴力调了好长时间换成kmp过了...

我也不知道为何玄学事情总在我身上发生...

显然f[i][j] = Σ(i:1~m-1)f[i-1][k] * g[k][j]。

意思就是f[i][j]是从i-1位加一个数转移过来的。

n范围1e9,考虑矩阵加速...

我们的矩阵是个这个样子:

g[0][0]  g[1][0]  g[2][0]  ...  g[m-2][0]  g[m-1][0]           f[i][0]    f[i+1][0]

g[0][1]  g[1][1]  g[2][1]  ...  g[m-2][1]  g[m-1][0]           f[i][1]    f[i+1][1]

...                        *     ...           =       ...

...                      ...        ...

g[m-1][1]...                  f[i][m-1]   f[i+1][m-1]

然后快乐地转移...就做完了... 

吐槽一句:打矩阵好累啊

 

 


 

 

 

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
using namespace std;
inline int read(){
    int res=0;char ch=getchar();
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch)){res=(res<<3)+(res<<1)+(ch^48);ch=getchar();}
    return res; 
}
int n, m, mod;
int g[25][25], nxt[25];
int str[25];
int ans;

struct Mat{
    int a[25][25];
    Mat(){memset(a, 0, sizeof a);}
    void init(){for(int i=0;i<25;i++)a[i][i]=1;}
    friend Mat operator * (Mat x, Mat y)
    {
        Mat z;
        for (int k = 0 ; k < 25 ; k ++)
        {
            for (int i = 0 ; i < 25 ; i ++)
            {
                for (int j = 0 ; j < 25 ; j ++)
                {
                    z.a[i][j] = (z.a[i][j] + x.a[i][k] * y.a[k][j]) % mod;
                }
            }
        }
        return z;
    }
    friend Mat operator ^ (Mat x, int y)
    {
        Mat z;z.init();
        while(y)
        {
            if (y&1) z = z * x;
            x = x * x;
            y >>= 1;
        }
        return z;
    }
}A, res, B;

int main()
{
    n = read(), m = read(), mod = read();
    for (int i = 1 ; i <= m ; i ++) scanf("%1d", &str[i]);
    int p = 0;
    for(int i = 2 ; i <= m ; i ++)
    {
        while(p and str[p+1] != str[i])p = nxt[p];
        if(str[p+1]==str[i])p++;
        nxt[i]=p;
    }
    for (int i = 0 ; i < m ; i ++)
    {
        for (int j = 0 ; j <= 9 ; j ++) 
        {
            int k = i;
            while(k and str[k+1] != j) k=nxt[k];
            if (str[k+1] == j) k++;
            if(k != m) g[i][k] ++;
            if (g[i][k] >= mod) g[i][k] -= mod;
        }
    }
    for (int i = 0 ; i <= m - 1 ; i ++)
    {
        for (int j = 0 ; j <= m - 1 ;  j ++)
        {
            A.a[i][j] = g[j][i];
        }
    }
    B.a[0][0] = 1;
    res = A ^ n;
    res = res * B;
    for (int i = 0 ; i <= m - 1 ; i ++) ans = (ans + res.a[i][0]) % mod;
    cout<<ans<<endl;
    return 0;
} 

 

 

 

posted @ 2018-07-25 11:50  zZhBr  阅读(143)  评论(0编辑  收藏  举报