[CF] D. Magic Numbers - Educational Codeforces Round 8

https://codeforces.com/contest/628/problem/D

题目大意

给出 $m \ (1 \le m \le 2000)$ 和数字 $d \ (0 \le d \le 9)$,给出两个字符串表示的没有前导零的大整数 $a, b \ (1 \le a \le b \le 10^2000)$。 问在区间 $[a, b]$ 中有多少数字 $x$ 符合能被 $m$ 整除,且从高向低数的偶数位($1-based$),都是 $d$ 且奇数位都不是 $d$。

简要题解

一眼数位 dp,分比较高可能是因为比赛里面确实比较难较快的写完或者调出来。

考虑 $dp[cur][rm][odd \ 0/1][lim \ 0/1][zero \ 0/1]$ 表示当前考虑了高 $cur + 1$ 位,目前余数是 $rm$,去除前导零后当前位是 $odd$ 奇数位(对应题中 $1-based$ 的偶数位),顶到了上限 $lim$,之前的位都是前导零 $zero$。转移比较直观,详见代码。

特别注意:这题因为状态开的比较满,因此如果开得变量比较随性的话,可能喜提 MLE。以下为某 MLE 部分代码。简单计算它至少用了 $384MB$ 的空间,超出了 $256MB$ 的限制。

const int MM = 2000;
const int MN = 2000;

bool check(string s, int m, int d) {
  int cur = 0;
  for (char c : s) {
    cur = (cur * 10 + c - '0') % m;
  }
  if (cur) return false;

  int n = s.length();
  for (int i = 1; i < n; i += 2) {
    if (s[i] - '0' != d) return false;
  }
  for (int i = 0; i < n; i += 2) {
    if (s[i] - '0' == d) return false;
  }
  return true;
}

Mint dp[MN][MM][2][2][2]; // struct { long long x; }
int vis[MN][MM][2][2][2]; // 384,000,000
vector<int> a;
int n, m, d;
Mint dfs(int cur, int rm, int odd, int lim, int zero) {
  if (cur == n) {
    return !zero && rm == 0;
  }

  if (vis[cur][rm][odd][lim][zero]) {
    return dp[cur][rm][odd][lim][zero];
  }

  vis[cur][rm][odd][lim][zero] = 1;
  Mint& ans = dp[cur][rm][odd][lim][zero];
  ans = 0;
  
  int up = lim ? a[cur] : 9;

  if (zero) {
    ans += dfs(cur + 1, 0, 0, 0, 1);
    
    for (int i = 1; i <= up; i++) {
      if (i == d) continue;
      ans += dfs(cur + 1, i % m, 1, lim && i == up, 0);
    }
    return ans;
  }

  // zero false!

  if (odd) { // need to fill a d
    if (lim) {
      if (a[cur] < d) {
        ans = Mint(0);
      } else {
        ans = dfs(cur + 1, (rm * 10 + d) % m, 0, d == up, 0);
      }
    } else {
      ans = dfs(cur + 1, (rm * 10 + d) % m, 0, 0, 0);
    }
  } else {
    for (int i = 0; i <= up; i++) {
      if (i == d) continue;
      ans += dfs(cur + 1, (rm * 10 + i) % m, 1, lim && i == up, 0);
    }
  }

  return ans;
}

Mint solve(string s, int mm, int dd) {
  n = s.length();
  m = mm;
  d = dd;
  a.clear();
  for (char c : s) {
    a.push_back(c - '0');
  }

  memset(vis, 0, sizeof(vis));
  Mint ans = dfs(0, 0, 0, 1, 1);
  // cerr << s << ' ' << mm << ' ' << dd << ' ' << ans << endl;
  return ans;
}

void solve() {
  int m, d; cin >> m >> d;
  string a, b; cin >> a >> b;

  cout << (solve(b, m, d) - solve(a, m, d) + check(a, m, d)) << '\n';
}

复杂度

$T$:$O(|B| m)$

$S$:$O(|B| m)$

代码实现

#include <bits/stdc++.h>
using namespace std;

int io_=[](){ ios::sync_with_stdio(false); cin.tie(nullptr); return 0; }();

using LL = long long;
using ULL = unsigned long long;
using LD = long double;
using PII = pair<int, int>;
using VI = vector<int>;
using MII = map<int, int>;

template<typename T> void cmin(T &x,const T &y) { if(y<x) x=y; }
template<typename T> void cmax(T &x,const T &y) { if(x<y) x=y; }
template<typename T> bool ckmin(T &x,const T &y) { 
    return y<x ? (x=y, true) : false; }
template<typename T> bool ckmax(T &x,const T &y) { 
    return x<y ? (x=y, true) : false; }
template<typename T> void cmin(T &x,T &y,const T &z) {// x<=y<=z 
    if(z<x) { y=x; x=z; } else if(z<y) y=z; }
template<typename T> void cmax(T &x,T &y,const T &z) {// x>=y>=z
    if(x<z) { y=x; x=z; } else if(y<z) y=z; }

// mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
// mt19937_64 rnd_64(chrono::system_clock::now().time_since_epoch().count());

/*
---------1---------2---------3---------4---------5---------6---------7---------
1234567890123456789012345678901234567890123456789012345678901234567890123456789
*/

const int MO = 1e9 + 7;
inline void addv(int& x, int y) {
  x += y;
  if (x >= MO) x -= MO;
}
inline void subv(int& x, int y) {
  x -= y;
  if (x < 0) x += MO;
}

const int MM = 2000;
const int MN = 2000;

bool check(string s, int m, int d) {
  int cur = 0;
  for (char c : s) {
    cur = (cur * 10 + c - '0') % m;
  }
  if (cur) return false;

  int n = s.length();
  for (int i = 1; i < n; i += 2) {
    if (s[i] - '0' != d) return false;
  }
  for (int i = 0; i < n; i += 2) {
    if (s[i] - '0' == d) return false;
  }
  return true;
}

int dp[MN][MM][2][2][2];
vector<int> a;
int n, m, d;
int dfs(int cur, int rm, int odd, int lim, int zero) {
  if (cur == n) {
    return !zero && rm == 0;
  }

  if (dp[cur][rm][odd][lim][zero] != -1) {
    return dp[cur][rm][odd][lim][zero];
  }

  int& ans = dp[cur][rm][odd][lim][zero];
  ans = 0;
  
  int up = lim ? a[cur] : 9;

  if (zero) {
    addv(ans, dfs(cur + 1, 0, 0, 0, 1));
    
    for (int i = 1; i <= up; i++) {
      if (i == d) continue;
      addv(ans, dfs(cur + 1, i % m, 1, lim && i == up, 0));
    }
    return ans;
  }

  // zero false!

  if (odd) { // need to fill a d
    if (lim) {
      if (a[cur] < d) {
        ans = 0;
      } else {
        ans = dfs(cur + 1, (rm * 10 + d) % m, 0, d == up, 0);
      }
    } else {
      ans = dfs(cur + 1, (rm * 10 + d) % m, 0, 0, 0);
    }
  } else {
    for (int i = 0; i <= up; i++) {
      if (i == d) continue;
      addv(ans, dfs(cur + 1, (rm * 10 + i) % m, 1, lim && i == up, 0));
    }
  }

  return ans;
}

int solve(string s, int mm, int dd) {
  n = s.length();
  m = mm;
  d = dd;
  a.clear();
  for (char c : s) {
    a.push_back(c - '0');
  }

  memset(dp, -1, sizeof(dp));
  int ans = dfs(0, 0, 0, 1, 1);
  // cerr << s << ' ' << mm << ' ' << dd << ' ' << ans << endl;
  return ans;
}

void solve() {
  int m, d; cin >> m >> d;
  string a, b; cin >> a >> b;

  int ans = solve(b, m, d);
  subv(ans, solve(a, m, d));
  addv(ans, check(a, m, d));

  cout << ans << '\n';
}

int main() {
  int t = 1; 
  // cin >> t;
  while (t--) {
    solve();
  }
  return 0;
}
Prev: [CF] D. Broken BST - Educational Codeforces Round 19
Next: [CF] B. New Skateboard - Educational Codeforces Round 8