Great learning for me:
https://www.hackerrank.com/rest/contests/master/challenges/lucky-numbers/hackers/turuthok/download_solution
Basically it is memorized search application. And we follow a discrete strategy: split it into digits and go digit by digit.
Here is my C++ version of the code above, with comments for easier understanding.
#include <iostream> #include <limits> #include <vector> using namespace std; typedef long long LL; const int D = 18; // max digit count const int MD = 9; // max single digit const LL MAX = std::numeric_limits<LL>::max(); vector<vector<vector<LL>>> dp(D, vector<vector<LL>>(D*MD + 1, vector<LL>(D*MD*MD + 1, -1))); vector<int> hi(D); vector<bool> isPrime(D*MD*MD + 1, true); LL go(int inx, int sd, int ssd, bool limited) { if (inx == D) return isPrime[sd] && isPrime[ssd] ? 1 : 0; // Memorized Search LL &ret = dp[inx][sd][ssd]; if (!limited && ret >= 0) return ret; // We only cache unlimited count int upper = limited ? hi[inx] : 9; // DP: count of current digit = sum of all counts of all previous digits LL r = 0; for (int i = 0; i <= upper; i++) { r += go(inx + 1, sd + i, ssd + i * i, limited && i == upper); } if (!limited) ret = r; // We only cache unlimited count return r; } LL go(LL n) { if (n <= 1) return 0; // split digits for (int i = D - 1; i >= 0; i--) { hi[i] = n % 10; n /= 10; } return go(0, 0, 0, true); } int main() { // sieving isPrime[0] = isPrime[1] = false; for (int i = 2; i <= D*MD*MD; i++) if (isPrime[i]) for (int j = i * 2; j <= D*MD*MD; j += i) isPrime[j] = false; // go int t; cin >> t; while (t--) { LL a, b; cin >> a >> b; cout << (go(b) - go(a - 1)) << endl; } return 0; }