[DP] CF1194G Another Meme Problem
数位 DP 好题!!!1
题出的好!难度适中,覆盖知识点广,题目又着切合实际的背景,解法比较自然。给出题人点赞 !
Link:https://codeforces.com/problemset/problem/1194/G
Problem Statement
time limit per test: 4 seconds
memory limit per test: 512 megabytes
Let's call a fraction \(\frac{x}{y}\) good if there exists at least one another fraction \(\frac{x'}{y'}\) such that \(\frac{x}{y} = \frac{x'}{y'}\), \(1 \le x', y' \le 9\), the digit denoting \(x'\) is contained in the decimal representation of \(x\), and the digit denoting \(y'\) is contained in the decimal representation of \(y\). For example, \(\frac{26}{13}\) is a good fraction, because \(\frac{26}{13} = \frac{2}{1}\).
You are given an integer number \(n\). Please calculate the number of good fractions \(\frac{x}{y}\) such that \(1 \le x \le n\) and \(1 \le y \le n\). The answer may be really large, so print it modulo \(998244353\).
Input
The only line of the input contains one integer \(n\) (\(1 \le n \le 10^{100}\)).
Output
Print the number of good fractions \(\frac{x}{y}\) such that \(1 \le x \le n\) and \(1 \le y \le n\). The answer may be really large, so print it modulo \(998244353\).
Solution
首先考虑计算 \(\dfrac{x'}{y'} = \dfrac{x}{y}\) 且满足条件的方案数。
为了避免算重,我们去掉对 \(\gcd(x, y) \neq 1\) 的方案统计,在统计 \((x, y)\) 的方案时加上 \(x'\) 的十进制表示含 \(xd\) 且 \(y'\) 的十进制表示含 \(yd\) 的方案。
发现 \((x', y')\) 可以写成 \((xz, yz)\) 的形式。所以考虑从低位往高位数位 DP,这样可以边枚举数边乘,然后边统计每个数是否出现,发现当 \(\max(x, y) \geq 2\) 时 \(1 \leq d \leq 4\),可以 \(2^4\) 个状态进行状压。
现在只剩下一个问题:如何处理上界。我们可以预处理出 \(\left\lfloor\dfrac{n}{1}\right\rfloor, \left\lfloor\dfrac{n}{2}\right\rfloor, \dots, \left\lfloor\dfrac{n}{9}\right\rfloor\),然后每次数位 DP,枚举 \(z\)(上界为 \(\left\lfloor\dfrac{n}{\max(x, y)}\right\rfloor\)),然后用两个 \(0 \sim 9\) 的变量存储进位。考虑 \(\overline{a_{1}a_{2}\dots a_{n}} \leq \overline{b_{1}b_{2}\dots b_{n}}\) 的条件是什么。第一种情况是对于所有的 \(i(1 \leq i \leq n)\) 都满足 \(a_i = b_i\);第二种情况是存在一个 \(i(1 \leq i \leq n)\) 满足对于所有的 \(j(1 \leq j < i)\) 有 \(a_j = b_j\) 且 \(a_i < b_i\)。所以考虑从低位到高位遍历的时候存下满足 \(a_i \neq b_i\) 最小的 \(i\)(如果没有的话则为 \(0\)),最后如果 \(i = 0\) 或 \(a_i < b_i\) 则满足上界条件。
然后对于 \(x = 1, y = 1\) 的情况,容易发现对于所有的 \(i(1 \leq i \leq n)\),\(\dfrac{i}{i}\) 都满足情况,所以贡献为 \(n\)。至此我们解决了本题。考虑到 \(\max(x, y) \leq 2\) 的情况(不含 \(x = 1, y = 1\))只有 \(2\) 种,状压有 \(256\) 种状态。剩下的情况的状态数 最多为 \(\textbf{64}\),所以跑的很快,这里不作具体的时间复杂度分析。反正只有个别情况需要一些时间其他的都很快。
Code
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/rope>
bool Mbe;
typedef long long i64;
typedef unsigned long long u64;
typedef __int128 i128;
std :: mt19937 rnd (std :: time (0));
template < class T > T gen (const T l, const T r) {return rnd () % (r - l + 1) + l;}
const double pi = std :: acos (-1);
template < class T > inline void read (T &val) {
T x = 0;
bool f = false;
char c = std :: getchar ();
for ( ; c < '0' || c > '9' ; c = std :: getchar ()) f |= (c == '-');
for ( ; c >= '0' && c <= '9' ; c = std :: getchar ()) x = (x << 1) + (x << 3) + (c & 15);
val = (!f) ? (x) : (-x);
return ;
}
#define debug(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
const i64 hashmod1 = 2147483579, hashmod2 = 1610612741, hashmod3 = 805306457;
template < class T, class I > void chkmin (T &p, I q) {T q_ = q; p = std :: min (p, q_); return ;}
template < class T, class I > void chkmax (T &p, I q) {T q_ = q; p = std :: max (p, q_); return ;}
const i64 mod = 998244353;
char s[125];
int gcd (int a, int b) {return b == 0 ? a : gcd (b, a % b);}
int n, a[125], d[10][125];
i64 f[125][10][10][16][16][3];
i64 query (int x, int y) {
int z = std :: max (x, y);
int lim = 9 / z;
for (int i = 0;i <= n + 1; ++ i) {
for (int j = 0;j < 10; ++ j) {
for (int k = 0;k < 10; ++ k) {
for (int l = 0;l < (1 << lim); ++ l) {
for (int o = 0;o < (1 << lim); ++ o) {
for (int p = 0;p < 3; ++ p) {
f[i][j][k][l][o][p] = 0;
}
}
}
}
}
}
f[n + 1][0][0][0][0][2] = 1;
for (int i = n + 1;i > 1; -- i) {
for (int j = 0;j < 10; ++ j) {
for (int k = 0;k < 10; ++ k) {
for (int l = 0;l < (1 << lim); ++ l) {
for (int o = 0;o < (1 << lim); ++ o) {
for (int p = 0;p < 3; ++ p) {
if (!f[i][j][k][l][o][p]) continue;
for (int w = 0;w < 10; ++ w) {
int v[4] = {j + w * x, k + w * y, (j + w * x) % 10, (k + w * y) % 10};
int newp = p;
if (w < d[z][i - 1]) newp = 0;
if (w > d[z][i - 1]) newp = 1;
int l_ = l, o_ = o;
if (v[2] % x == 0 && v[2] && v[2] / x <= lim) l_ |= (1 << (v[2] / x - 1));
if (v[3] % y == 0 && v[3] && v[3] / y <= lim) o_ |= (1 << (v[3] / y - 1));
#define val f[i - 1][v[0] / 10][v[1] / 10][l_][o_][newp]
val = (val + f[i][j][k][l][o][p]) % mod;
#undef val
}
}
}
}
}
}
}
i64 ans = 0;
for (int i = 0;i < 10; ++ i) {
for (int j = 0;j < 10; ++ j) {
for (int k = 0;k < (1 << lim); ++ k) {
for (int l = 0;l < (1 << lim); ++ l) {
int k_ = k, l_ = l;
if (i % x == 0 && i && i / x <= lim) k_ |= (1 << (i / x - 1));
if (j % y == 0 && j && j / y <= lim) l_ |= (1 << (j / y - 1));
if (k_ & l_) ans = (ans + f[1][i][j][k][l][0] + f[1][i][j][k][l][2]) % mod;
}
}
}
}
return ans;
}
bool Med;
signed main () {
debug ("%.8lf MB\n", (&Mbe - &Med) / 1048576.0);
scanf ("%s", s + 1);
n = std :: strlen (s + 1);
i64 nmodp = 0;
for (int i = 1;i <= n; ++ i) a[i] = s[i] - '0', nmodp = (nmodp * 10ll + 1ll * a[i]) % mod;
for (int i = 1;i <= 9; ++ i) {
int cur = 0;
for (int j = 1;j <= n; ++ j) {
cur = cur * 10 + a[j];
d[i][j] = std :: min (cur / i, 9);
cur -= d[i][j] * i;
}
}
i64 ans = nmodp;
for (int x = 1;x <= 9; ++ x) {
for (int y = 1;y <= 9; ++ y) {
if (x == 1 && y == 1) continue;
if (gcd (x, y) > 1) continue;
ans = (ans + query (x, y)) % mod;
}
}
printf ("%lld\n", ans);
debug ("%.8lf ms\n", 1e3 * clock () / CLOCKS_PER_SEC);
return 0;
}
// g++ "a.cpp" -o a -std=c++14 -O2 -Wall -Wextra -Wshadow
// check! no inline! (except fastIO)