嘘~ 正在从服务器偷取页面 . . .

洛谷P3193 [HNOI2008]GT考试 题解


洛谷P3193 [HNOI2008]GT考试 题解

题目链接:P3193 [HNOI2008]GT考试

题意

阿申准备报名参加 GT 考试,准考证号为 $N$ 位数$X_1,X_2…X_n(0\le X_i\le9)$,他不希望准考证号上出现不吉利的数字。
他的不吉利数字$A_1,A_2…A_m(0\le A_i\le 9)$ 有 $M$ 位,不出现是指 $X_1,X_2…X_n$ 中没有恰好一段等于 $A_1,A_2…A_m$,$A_1$ 和$X_1$ 可以为 $0$

$N\leq10^9,M\leq20,K\leq1000$

显然是矩阵快速幂优化dp(看数据范围)。

设 $f_{i,j}$ 表示考虑到第 $i$ 个数,匹配到 $X$ 的第 $j$ 位时的方案数,则

注意这里的 $0\le j < m$ ,因为我们不能让它包含完整的 $X$ 。

这个 $p$ 不一定是 $0$ 或 $j-1$ ,根据KMP的性质可以很容易发现。

看上去这个可以用KMP来搞搞,又长的很像矩阵快速幂的题目

于是尝试套路地构造出预处理数组 $g_{i,j}$ ,以产生矩阵快速幂的形式

设 $g_{i,j}$ 表示匹配到 $X$ 的第 $i$ 位,有多少种加字符 $0\sim9$ 的方法可以使其匹配到 $j$

注意到题目中 $X_1$ 是可以为 $0$ 的,因此不用单独考虑前导零的问题,则

注意这里 $k$ 的意义改变为了:枚举已经匹配的长度,尝试加一个字符转移至 $j$ 。

然后把式子看成这样的形式 $f^{i}_{j} =\sum f^{i-1}_{k} \times g_{k,j}$ ,显然与矩阵乘法的定义相同

我们把 $f$ 和 $g$ 看作两个矩阵(注意 $f$ 是行矩阵),则有

然后我们矩阵快速幂求一下就好啦

最后答案就是 $\sum\limits_{i=0}^{m-1} f^{n}_{i}$ ,没有 $m-1$ 的原因是,不能包含 $m$

时间复杂度 $O(m^3 \log n)$

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdarg>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N (int)(25)
typedef vector< vector <int> > mat;

char s[N];
int n,m,mod,fail[N];
void add(int &x,int y){ (x+=y) >= mod ? x-=mod : 0;}
mat mul(mat A,mat B)
{
    int n=A.size(),m=A[0].size(),p=B[0].size();
    mat C(n,vector<int>(p));
    for(int i=0; i<m; i++)
        for(int j=0; j<n; j++)
            for(int k=0; k<p; k++)
                add(C[i][j],A[i][k]*B[k][j]%mod);
    return C;
}
mat qpow(mat A,int b)
{
    int n=max(A.size(),A[0].size());
    mat B(n,vector<int>(n));
    for(int i=0; i<n; i++) B[i][i]=1;
    for(; b; b>>=1)
    {
        if(b&1) B=mul(B,A);
        A=mul(A,A);
    }
    return B;
}
mat kmp()
{
    for(int i=2,j=0; i<=m; i++)
    {
        while(j&&s[i]!=s[j+1]) j=fail[j];
        if(s[i]==s[j+1])++j;
        fail[i]=j;
    }
    mat A(m,vector<int>(m));
    for(int i=0,j; i<m; i++)
        for(int c='0'; c<='9'; c++)
        {
            j=i; while(j&&s[j+1]!=c) j=fail[j];
            if(s[j+1]==c) ++j;
            add(A[i][j],1);
        }
    return A;
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> n >> m >> mod >> (s+1);
    mat G=kmp(); G=qpow(G,n);
    int res=0;
    for(int i=0; i<m; i++) add(res,G[0][i]);
    cout << res << '\n';
    return 0;
}

文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录