CF585F Digits of Number Pi 题解

作者: xht37 分类: 题解 发布时间: 2019-12-01 11:49

点击数:97

CF585F Digits of Number Pi

题意

  • 给定长度为 $n$ 的数字串 $s$ 和长度为 $d$ 的不含前导零的数字串 $x,y(x \le y)$。
  • 存在长度至少为 $\left\lfloor\frac{d}{2}\right\rfloor$ 的子串是 $s$ 的子串的数字串 $t \in [x,y]$ 的数量。
  • $n \le 10^3$,$d \le 50$,答案对 $10^9+7$ 取模。

题解

把 $s$ 所有长度为 $\left\lfloor\frac{d}{2}\right\rfloor$ 的子串拿出来建 AC 自动机,然后数位 dp 即可。

设 $f[i][j][1/0]$ 表示考虑到第 $i$ 位,在 AC 自动机的 $j$ 节点,是否 $(1/0)$ 有上界限制时的方案数,预处理出 $p[i][1/0]$ 为第 $i \sim d$ 位是否 $(1/0)$ 有上界限制时的方案数。

初始时 $f[0][1][1] = 1$。

枚举下一位的数字 $k$,设 $j\to k$ 为 AC 自动机中 $j$ 节点的第 $k$ 个儿子,设限制数字为 $c$。

  • 任意时刻有 $f[i][j][0] \to f[i+1][j\to k][0]$;
  • 若 $k < c$,则 $f[i][j][1] \to f[i+1][j\to k][0]$;
  • 若 $k=c$,则 $f[i][j][1] \to f[i+1][j\to k][1]$;

最后用乘法原理将每种方案的 $f,p$ 相乘即可,时间复杂度 $\mathcal O(10nd^2)$。

代码

const int N = 1e3 + 7, D = 53, K = 11;
int n, d, trie[N*D][K], ed[N*D], fail[N*D], tot = 1;
char s[N], a[D], b[D];
modint p[N][2], f[D][N*D][2];

inline void build() {
    int m = d / 2;
    for (int i = 1; i + m - 1 <= n; i++) {
        int p = 1;
        for (int j = i, k = 1; k <= m; j++, k++) {
            int c = s[j] - '0';
            if (!trie[p][c]) trie[p][c] = ++tot;
            p = trie[p][c];
        }
        ed[p] = 1;
    }
    queue< int > q;
    for (int i = 0; i < 10; i++)
        if (trie[1][i]) fail[trie[1][i]] = 1, q.push(trie[1][i]);
        else trie[1][i] = 1;
    while (q.size()) {
        int x = q.front();
        q.pop();
        for (int i = 0; i < 10; i++)
            if (trie[x][i]) fail[trie[x][i]] = trie[fail[x]][i], q.push(trie[x][i]);
            else trie[x][i] = trie[fail[x]][i];
    }
}

inline modint calc(char *t) {
    p[d+1][0] = p[d+1][1] = 1;
    for (int i = d; i; i--) p[i][0] = p[i+1][0] * 10, p[i][1] = (t[i] - '0') * p[i+1][0] + p[i+1][1];
    modint ans = 0;
    for (int i = 1; i <= d; i++)
        for (int j = 1; j <= tot; j++)
            for (int k = 0; k < 2; k++)
                f[i][j][k] = 0;
    f[0][1][1] = 1;
    for (int i = 0; i < d; i++)
        for (int j = 1; j <= tot; j++)
            if (!ed[j])
                for (int k = 0; k < 10; k++) {
                    int o = trie[j][k];
                    f[i+1][o][0] += f[i][j][0];
                    if (k < t[i+1] - '0') f[i+1][o][0] += f[i][j][1];
                    if (k == t[i+1] - '0') f[i+1][o][1] += f[i][j][1];
                }
    for (int i = 1; i <= d; i++)
        for (int j = 1; j <= tot; j++)
            if (ed[j]) ans += f[i][j][0] * p[i+1][0] + f[i][j][1] * p[i+1][1];
    return ans;
}

int main() {
    rds(s, n), rds(a, d), rds(b, d), build();
    int k = d;
    while (a[k] == '0') a[k--] = '9';
    --a[k];
    print(calc(b) - calc(a));
    return 0;
}

发表评论

电子邮件地址不会被公开。 必填项已用*标注