CF585F Digits of Number Pi 题解

Description

给定长度为 nn 的数字串 ss 和长度为 dd 的不含前导零的数字串 x,y(xy)x,y(x \le y)

存在长度至少为 d2\left\lfloor\frac{d}{2}\right\rfloor 的子串是 ss 的子串的数字串 t[x,y]t \in [x,y] 的数量。

n103n \le 10^3d50d \le 50,答案对 109+710^9+7 取模。

Solution

先把 ss 所有长度为 d2\left\lfloor\frac{d}{2}\right\rfloor 的子串拿出来,那么相当于要求这些子串必须在 tt 里出现,考虑用总数减去没出现的数量。

考虑用 [0,y][0,y] 的答案减去 [0,x1][0,x-1] 的答案,这样就只有上界了。

注意到这里是很多个字符串匹配一个串,考虑把 ss 的这些子串加到 AC 自动机里,那么这些子串没在 tt 里出现当且仅当从前往后扫 tt 的每一个字符,每次让 curtriecur,ticur\leftarrow trie_{cur,t_i},如果这里的 curcur 每次都不在 ss 子串对应的节点上就说明子串没在 tt 里出现。

这样就可以 dp 了。设 fi,j,0/1f_{i,j,0/1} 表示 1i11\sim i-1 走到 jjini\sim n 每次不走子串对应节点,后面有没有卡上界的限制的方案数。

转移就每次枚举 tit_i 即可。

时间复杂度:O(Σ2nd2)O\left(|\Sigma|^2nd^2\right)Σ|\Sigma| 为字符集大小。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e3 + 5, kMaxD = 55, kMaxT = kMaxN * kMaxD * 5, kMod = 1e9 + 7;

int n, d, tot;
int a[kMaxN], trie[kMaxT][10], fail[kMaxT], f[kMaxD][kMaxT][2];
bool tag[kMaxT], vis[kMaxD][kMaxT][2];
std::string s, L, R;

inline int add(int x, int y) { return (x + y >= kMod ? x + y - kMod : x + y); }
inline int sub(int x, int y) { return (x >= y ? x - y : x - y + kMod); }
inline void inc(int &x, int y) { (x += y) >= kMod ? x -= kMod : x; }
inline void dec(int &x, int y) { (x -= y) < 0 ? x += kMod : x; }

std::string sub(std::string s) {
for (int i = d; i; --i) {
if (s[i] != '0') {
--s[i];
for (int j = i + 1; j <= d; ++j) s[j] = '9';
break;
}
}
return s;
}

void ins(std::string s) {
int cur = 0;
for (auto c : s) {
int k = c - '0';
if (!trie[cur][k]) trie[cur][k] = ++tot;
cur = trie[cur][k];
}
tag[cur] = 1;
}

void build() {
std::queue<int> q;
for (int i = 0; i < 10; ++i) {
if (trie[0][i]) {
q.emplace(trie[0][i]);
}
}
for (; !q.empty();) {
int u = q.front(); q.pop();
for (int i = 0; i < 10; ++i) {
if (trie[u][i]) {
fail[trie[u][i]] = trie[fail[u]][i];
q.emplace(trie[u][i]);
} else {
trie[u][i] = trie[fail[u]][i];
}
}
}
}

int dfs(int x, int k, bool op) {
if (x > d || tag[k]) return !tag[k];
else if (vis[x][k][op]) return f[x][k][op];
int ret = 0;
for (int i = 0; i <= (op ? a[x] : 9); ++i) {
inc(ret, dfs(x + 1, trie[k][i], op && (i == a[x])));
}
vis[x][k][op] = 1;
return f[x][k][op] = ret;
}

int solve(std::string t) {
for (int i = 1; i <= d; ++i) a[i] = t[i] - '0';
int ans = 0;
for (int i = 1; i <= d; ++i) ans = (10ll * ans + a[i]) % kMod;
memset(f, 0, sizeof(f)), memset(vis, 0, sizeof(vis));
return sub(ans, dfs(1, 0, 1));
}

void dickdreamer() {
std::cin >> s >> L >> R;
n = s.size(), d = L.size();
s = " " + s, L = " " + L, R = " " + R;
for (int i = 1; i <= n - (d / 2) + 1; ++i)
ins(s.substr(i, d / 2));
build();
std::cout << sub(solve(R), solve(sub(L))) << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

CF585F Digits of Number Pi 题解
https://sobaliuziao.github.io/2024/07/26/post/e8f6081.html
作者
Egg_laying_master
发布于
2024年7月26日
许可协议