[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)
posted @ 2024-02-15 00:01  CountingGroup  阅读(21)  评论(0编辑  收藏  举报