P3193 [HNOI2008] GT考试 解题报告

题目传送门

题目大意:

给定一个长度为 m 且只含 09 的字符串 s,求出所有长度为 n 的,只含 09 且不含 s 字符串的数量,结果对 mod 取模。

数据范围:n109,m20,k1000

思路:

不难发现和 这道题 很像,只是 n 的数据范围被扩大到了 109m 的范围被缩小到了 20

朴素 dp 思路在 状态机模型 dp 中写了,这里只讲优化。

朴素做法的时间复杂度为 O(nm),由于这道题的 n 很大,所以 TLE 无疑了。

观察一下状态转移方程:

f(i+1,δ(π(j),ch))f(i,j)

对于每个 j,它有 10 种状态,分别是字符 09,而若 j 和将要填的字符 ch 一定,那么 δ(π(j),ch) 显然也一定,即转移到的地方也相同,而这种转移对于每一个 i 都要做一次,所以应该优化这个过程。

把状态转移方程展开,以 f(i+1,0m1) 这一层为例:

{f(i+1,0)=a0,0×f(i,0)+a1,0×f(i,1)++am1,0×f(i,m1)f(i+1,1)=a0,1×f(i,0)+a1,1×f(i,1)++am1,1×f(i,m1)f(i+1,m1)=a0,m1×f(i,0)+a1,m1×f(i,1)++am1,m1×f(i,m1)

这不就是矩阵乘法的形式吗?

根据上面的分析,我们构造的矩阵 Ai 无关,所以设 F(i)=[f(i,0)f(i,1)f(i,m1)],那么 F(i+1)=F(i)×A,不难推出 F(n)=F(0)×An,然后就可以用矩阵快速幂算了。

现在的问题变成了如何构造矩阵 A

根据状态转移方程,若 f(i,j)f(i+1,p),那么说明要得到 f(i+1,p) 要加上 f(i,j),即 A 矩阵中的第 jp 列要加上 1

为什么呢,看这张图就明白了:

其实图是我偷的(

那么矩阵 A 就构造好了,这道题也做完了。

流程:

先预处理 KMP 的 ne 数组,然后预处理出 A 矩阵,最后直接矩阵快速幂输出答案即可。

时间复杂度:O(m3logn)

Code:

#include <cstring>
#include <iostream>

using namespace std;

const int N = 25;

int n, m, mod;
char s[N];
int ne[N];

struct Matrix {
    int mar[N][N];
    Matrix() {memset(mar, 0, sizeof mar);}
    void init() {
        memset(mar, 0, sizeof mar);
        for(int i = 0; i < m; i++)
            mar[i][i] = 1;
    }
    Matrix operator *(const Matrix &o) const {
        Matrix res;
        for(int i = 0; i < m; i++)
            for(int j = 0; j < m; j++)
                for(int k = 0 ; k < m; k++)
                    res.mar[i][j] = (res.mar[i][j] + mar[i][k] * o.mar[k][j]) % mod;
        return res;
    }
}A;

int qpow(int k) {
    Matrix res;
    res.mar[0][0] = 1;
    while(k) {
        if(k & 1) res = res * A;
        A = A * A;
        k >>= 1;
    }
    int ans = 0;
    for(int i = 0; i < m; i++)
        ans = (ans + res.mar[0][i]) % mod;
    return ans;
}

int main() {
    scanf("%d%d%d%s", &n, &m, &mod, s + 1);
    for(int i = 2, j = 0; i <= m; i++) {
        while(j && s[j + 1] != s[i]) j = ne[j];
        if(s[j + 1] == s[i]) j++;
        ne[i] = j;
    }
    for(int j = 0; j < m; j++)
        for(int k = '0'; k <= '9'; k++) {
            int p = j;
            while(p && s[p + 1] != k) p = ne[p];
            if(s[p + 1] == k) p++;
            if(p < m) A.mar[j][p]++;
        }
    printf("%d\n", qpow(n));
    return 0;
}
posted @   Brilliant11001  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示