---
# System prepended metadata

title: Help Conan 10! - Solution

---

# Help Conan 10! - Solution


## 1. Quan sát

Theo đề bài, chúng ta cần tìm số các số $x$ thỏa mãn:
$$A <= x * f(x) <= B$$ 

với $f(x)$ là tích các chữ số của $x$. 

$$ {A \over f(x)} <= x <= {B \over f(x)} $$

Dễ thấy rằng do $A >= 1$ nên $x \ne  0$ và $f(x) \ne 0$, suy ra mọi số $x$ thỏa mãn sẽ không chứa chữ số $0$ $(1)$

Do tích các chữ số của một số luôn nhỏ hơn hoặc bằng chính số đó nên $f(x) <= x$, suy ra 

$$f(x)^2 <= x * f(x) <= 1e18$$ hay $f(x) <= 1e9$ $(2)$

Vì $f(x)$ ở đây là tích các chữ số từ $[1, 9]$ nên:
$$f(x) = 1^a . 2^b . 3^c . 4^d . 5^e . 6^f . 7^g . 8^h . 9^i = 2^{a_1} . 3^{a_2} . 5^{a_3} . 7^{a_4} (3)$$

Kết hợp cùng nhận xét $(2)$, nếu for qua 4 vòng ta nhận thấy rằng chỉ có đúng $5194$ bộ số $a_1, a_2, a_3, a_4$ phân biệt thỏa mãn điều kiện trên, ta hoàn toàn có thể lưu trước $5194$ tích đó thành một mảng.

Giờ đây bài toán rút gọn về: 

- Với mỗi tích $f(x)$, cho $A' = {A \over f(x)}$, $B' = {B \over f(x)}$, có bao nhiêu số $x$ thỏa mãn:

$$A' <= x <= B'$$

## 2. Thuật trâu

Dễ thấy rằng ta có thể chạy 1 vòng từ $A'$ đến $B'$ rồi đếm xem có bao nhiêu số thỏa mãn. Tuy nhiên $x <= 1e18$ là quá lớn nên cách này sẽ TLE.

## 3. Lời giải

Đây là một biến thể của bài toán: Đếm số các số có tích các chữ số bằng $k$, vì giới hạn của bài là $1e18$, nên sẽ chỉ có tối đa $18$ chữ số, mỗi chữ số có $9$ cách chọn (từ nhận xét $(1)$).

Ta có thể sử dụng kỹ thuật **Digit DP** để xử lý bài toán này, gọi **dp[n][product]** là số các số có **n** chữ số và tích các chữ số bằng **product**, do tích các chữ số của một số có thể rất lớn nên từ nhận xét $(3)$, ta có thể tách bớt ra thành **dp[n][a1][a2][a3][a4]**.

Ta có thể giải quyết mỗi bài toán dp trên bằng đệ quy: **solve(n, num, num_digit)** là số các số thỏa mãn có **n** chữ số, **num** lưu số ta đã chọn ở lần đệ quy trước và **num_digit** lưu số vị trí còn nhét được thêm.

Độ phức tạp: $O(5194 * g(B))$ với $g(B) =$ số chữ số của $B$.

## Code:
```
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll maxn = 2e5 + 5;
const ll maxn_sqrt = 1e9;
const ll maxn_ll = 1e18;
const ll mod = 1e9 + 7;

ll binpow(ll a, ll b)
{
    ll r = 1;

    for (;b; a = a * a, b >>= 1)  if (b & 1) r = r * a; return r;
}
ll dp[19][30][30][30][30];
ll ans = 0;
ll current[4] = {0, 0, 0, 0};
// 2^a1 * 3^a2 * 5^a3 * 7^a4
ll base[10][4] = 
{
   {0, 0, 0, 0}, // 1
   {0, 0, 0, 0}, // 1
   {1, 0, 0, 0}, // 2
   {0, 1, 0, 0}, // 3
   {2, 0, 0, 0}, // 4
   {0, 0, 1, 0}, // 5
   {1, 1, 0, 0}, // 6
   {0, 0, 0, 1}, // 7
   {3, 0, 0, 0}, // 8
   {0, 2, 0, 0}  // 9
};

bool validate(ll min_num, ll max_num, ll l, ll r)
{
	if (min_num >= l && max_num <= r) return true;
	return false;
}

ll solve(ll n, ll num, ll num_digit, ll l, ll r)
{
	if (num > r || (num + num_digit - 1) < l) return 0;
	if (n >= 18) 
	{
		//cout << current[0] << ' ' << current[1] << ' ' << current[2] << ' ' << current[3] << '\n';
		return !current[0] && !current[1] && !current[2] && !current[3];
	}
	
	if (validate(num, (num + num_digit - 1), l, r) && dp[n][current[0]][current[1]][current[2]][current[3]] != -1) return dp[n][current[0]][current[1]][current[2]][current[3]];
	// kiểm tra trường hợp số đang xét có quá khoảng l r cần biết không (max là nhét thêm toàn chữ số 9 vào)
	ll cnt = 0;
	ll start;
	if (num == 0) start = 0;
	else start = 1;
	
	for (ll i = start; i <= 9; ++i)
	{
		bool flag = true; 
		for (ll j = 0; j < 4; ++j)
		{
			if (base[i][j] > current[j])
			{
				flag = false;
				break;
			}	
		}	
		if (!flag) continue;
		
		for (ll j = 0; j < 4; ++j) current[j] -= base[i][j]; // Giảm số mũ của chữ số đã chọn xuống
		ll addnew_num = (i * (num_digit / 10));
		cnt += solve(n + 1, num + addnew_num, (num_digit / 10), l, r); // Nhét chữ số đã chọn và đệ quy đến trường hợp nhỏ hơn tiếp theo
                                                                       
		for (ll j = 0; j < 4; ++j) current[j] += base[i][j]; //Reset lại số mũ lũy thừa về base case ban đầu, xét các chữ số tiếp theo
	}
	
	if (validate(num, (num + num_digit - 1), l, r)) 
	{
		dp[n][current[0]][current[1]][current[2]][current[3]] = cnt; // lưu trường hợp đệ quy vào dp
		return dp[n][current[0]][current[1]][current[2]][current[3]]; 
	}
	else return cnt;
	
}

int main()
{
	memset(dp, -1, sizeof(dp));
	ll l, r;
	cin >> l >> r;

    //tính hết mọi bộ số có tích thỏa mãn
    //do chỉ có 5194 tích thỏa mãn nên lưu trước vào một mảng riêng hoặc gen bằng đệ quy cũng được
    for (ll i = 0; i <= 30; ++i)
    {
        for (ll j = 0; j <= 30; ++j)
        {
            for (ll k = 0; k <= 30; ++k)
            {
                for (ll q = 0; q <= 30; ++q)
                {
                    ll n = binpow(2, i) * binpow(3, j) * binpow(5, k) * binpow(7, q);
                    if (n <= maxn_sqrt && n > 0) 
                    {
                        current[0] = i;
                        current[1] = j;
                        current[2] = k;
                        current[3] = q;
                        // do x có thể bé hơn n (x / n = 0)
                        ans += solve(0, 0, maxn_ll, (l + n - 1) / n, r / n); // a/x <= x <= b/x
                        //cout << n << '\n';

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


```

