1. 程式人生 > >【HNOI2008】【BZOJ1009】T考試

【HNOI2008】【BZOJ1009】T考試

【題目連結】

【前置技能】

  • DP
  • 矩陣乘法
  • AC自動機
  • KMP

【題解】

  • 預處理出每一位後面填00~99可以走到哪一位,DP狀態:f[i][j]f[i][j]表示現在是第ii位數字,匹配到第jj位。轉移比較顯然,不多贅述。因為nn比較大,所以要用矩陣乘法優化轉移。這裡覺得AC自動機寫起來比較方便,所以程式碼給出的是AC自動機的寫法。
  • 時間複雜度O(M3logN)O(M^3logN)

【程式碼】

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL  long long
#define MAXN    22
#define MAXLOG  30
using namespace std;
int n, m, ans, mod;
char s[MAXN];
int mat[MAXLOG + 1][MAXN][MAXN], f[1][MAXN], tmp[1][MAXN];
 
template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
    x = 0; int f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}
 
void update(int &x, int y){
    x += y;
    if (x >= mod) x -= mod;
}
 
int mul(int x, int y){
    return 1ll * x * y % mod;
}
 
struct AC_Automaton{
    struct info{int son[10], fail, tag;}a[MAXN];
    int cnt;
    void ins(char *s){
        int len = strlen(s + 1);
        int pos = 0;
        for (int i = 1; i <= len; ++i){
            int ch = s[i] - '0';
            if (!a[pos].son[ch]) a[pos].son[ch] = ++cnt;
            pos = a[pos].son[ch];
        }
        a[pos].tag = 1;
    }
    void build(){
        static int q[MAXN], l = 0, r = -1;
        for (int ch = 0; ch < 10; ++ch)
            if (a[0].son[ch]) q[++r] = a[0].son[ch];
        while (l <= r){
            int pos = q[l++];
            for (int ch = 0; ch < 10; ++ch)
                if (a[pos].son[ch]) {
                    a[a[pos].son[ch]].fail = a[a[pos].fail].son[ch];
                    a[a[pos].son[ch]].tag |= a[a[a[pos].son[ch]].fail].tag;
                    q[++r] = a[pos].son[ch];
                } else a[pos].son[ch] = a[a[pos].fail].son[ch];
        }
    }
    int go(int pos, int ch){
        return a[pos].son[ch];
    }
    void work(){
        for (int pos = 0; pos <= cnt; ++pos){
            if (a[pos].tag) continue;
            for (int ch = 0; ch < 10; ++ch){
                int nxt = go(pos, ch);
                if (a[nxt].tag) continue;
                update(mat[0][pos][nxt], 1);
            }
        }
        for (int p = 1; p <= MAXLOG; ++p)
            for (int i = 0; i <= cnt; ++i)
                for (int j = 0; j <= cnt; ++j)
                    for (int t = 0; t <= cnt; ++t)
                        update(mat[p][i][j], mul(mat[p - 1][i][t], mat[p - 1][t][j]));
        f[0][0] = 1;
        for (int p = 1; p <= MAXLOG; ++p)
        if (n & (1 << p)) {
            memset(tmp, 0, sizeof(tmp));
            for (int i = 0; i <= 0; ++i)
                for (int j = 0; j <= cnt; ++j)
                    for (int t = 0; t <= cnt; ++t)
                        update(tmp[i][j], mul(f[i][t], mat[p][t][j]));
            for (int i = 0; i <= 0; ++i)
                for (int j = 0; j <= cnt; ++j)
                    f[i][j] = tmp[i][j];
        }
        ans = 0;
        for (int pos = 0; pos <= cnt; ++pos)
            if (!a[pos].tag) update(ans, f[0][pos]);
    }
}ACAM;
 
int main(){
    read(n), read(m), read(mod);
    scanf("%s", s + 1);
    ACAM.ins(s);
    ACAM.build();
    ACAM.work();
    printf("%d\n", ans);
    return 0;
}