Bzoj1009: [HNOI2008]GT考试

题面

传送门

Sol

\(f[i][j]\)表示到第\(i\)个数,最后\(j\)个为不吉利数字的前缀的方案数
于是就可以写一个\(KMP\)套暴力\(DP\)\(next\)转移

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;

IL ll Read(){
    RG ll x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, m, k, f[101010][30], nxt[30], ans;
char s[30];

int main(RG int argc, RG char* argv[]){
    n = Read(); m = Read(); k = Read();
    scanf(" %s", s + 1);
    for(RG int i = 2, j = 0; i <= m; ++i){
        while(j && s[i] != s[j + 1]) j = nxt[j];
        if(s[i] == s[j + 1]) nxt[i] = ++j;
    }
    f[0][0] = 1;
    for(RG int i = 1; i <= n; ++i)
        for(RG int j = 0; j < m; ++j){
            if(!f[i - 1][j]) continue;
            for(RG int l = '0'; l <= '9'; ++l){
                RG int p = j;
                while(p && l != s[p + 1]) p = nxt[p];
                if(s[p + 1] == l) ++p;
                f[i][p] = (f[i][p] + f[i - 1][j] % k) % k;
            }
        }
    for(RG int i = 0; i < m; ++i) ans = (ans + f[n][i]) % k;
    printf("%d\n", ans);
    return 0;
}

我们发现转移是一样的,预处理出来,\(n\)这么大那就矩阵乘法优化一下

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;

IL ll Read(){
    RG ll x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, m, k, nxt[30], ans;
char s[30];

struct Matrix{
    int a[30][30];

    IL void Clear(){  Fill(a, 0);  }
    
    IL void Init(){  Clear(); for(RG int i = 0; i < m; ++i) a[i][i] = 1;  }

    IL int* operator [](RG int x){  return a[x];  }

    IL Matrix operator *(RG Matrix B){
        RG Matrix C; C.Clear();
        for(RG int i = 0; i < m; ++i)
            for(RG int j = 0; j < m; ++j)
                for(RG int l = 0; l < m; ++l)
                    (C[i][l] += (1LL * a[i][j] * B[j][l] % k)) %= k;
        return C;
    }
} S, E;

int main(RG int argc, RG char* argv[]){
    n = Read(); m = Read(); k = Read();
    scanf(" %s", s + 1);
    for(RG int i = 2, j = 0; i <= m; ++i){
        while(j && s[i] != s[j + 1]) j = nxt[j];
        if(s[i] == s[j + 1]) nxt[i] = ++j;
    }
    for(RG int i = 0; i < m; ++i)
        for(RG int j = '0'; j <= '9'; ++j){
            RG int p = i;
            while(p && j != s[p + 1]) p = nxt[p];
            if(s[p + 1] == j) ++p;
            ++E[i][p];
        }
    S[0][0] = 1;
    for(RG int i = n; i; i >>= 1, E = E * E) if(i & 1) S = S * E;
    for(RG int i = 0; i < m; ++i) ans = (ans + S[0][i]) % k;
    printf("%d\n", ans);
    return 0;
}

转载于:https://www.cnblogs.com/cjoieryl/p/8318154.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值