数位小孩

传送门

题目描述

给出一个区间\([l,r]\), 求这个区间内有多少个数字满足如下条件:

  1. 每相邻两个数位和为素数

  2. 其中至少一个数位为 \(1\)

  3. 没有前导 \(0\)

数据范围

\(1 \le l \le r \le 10^{10}\)

思路

典型的 数位DP

计算方式为 \(f(r) - f(l-1)\)

\(f(x)\) 代表 \([0,x]\)中符合条件的数字,我们通过从高到低枚举 \(x\) 的每一位来进行记忆化搜索即可

不懂 数位DP 的可以去看下这篇blog

CODE
/********************
Author:  Nanfeng1997
Contest: NowCoder
URL:     https://ac.nowcoder.com/acm/problem/233129
When:    2022-03-15 14:21:34

Memory:  524288MB
Time:    2000ms
********************/
#include  <bits/stdc++.h>
using namespace std;
using LL = long long;

LL dp[11][11][2][2][2];
int a[11], cnt;
bool vis[20];

LL dfs(int pos, int last, bool limit, bool lead, bool flag) {
  if(pos == cnt) return flag;
  LL &ans = dp[pos][last][limit][lead][flag];
  if(ans != -1) return ans;  ans = 0;
  for(int i = 0; i <= (limit ? a[pos] : 9); i ++ ) {
    if(lead) {
      ans += dfs(pos + 1, i, limit && i == a[pos], lead && !i, flag || i == 1);
    } else if(vis[last + i]) {
      ans += dfs(pos + 1, i, limit && i == a[pos], lead && !i, flag || i == 1);
    }
  }
  return ans;
}

LL f(LL x) {
  if(!x) return x; 
  cnt = 0;
  memset(a, 0, sizeof a);
  memset(dp, -1, sizeof dp);
  while(x) a[cnt ++ ] = x % 10, x /= 10;
  reverse(a, a + cnt);
  return dfs(0, 0, true, true, false);
}

void solve() {
  vis[2] = 1, vis[3] = 1, vis[5] = 1, vis[7] = 1;
  vis[11] = 1, vis[13] = 1, vis[17] = 1;
  LL l, r; cin >> l >> r;
  cout << f(r) - f(l - 1) << "\n";
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int T = 1; //cin >> T;
  while(T --) solve();

  return 0;
}

posted @ 2022-03-15 14:49  ccz9729  阅读(45)  评论(0编辑  收藏  举报