題意
給定 ()和 (),求有多少小於 的正整數的最大質因子不超過 。
解析
答案是 級別的,直接算會爆炸,考慮最佳化暴力。
法 1
考慮使用 meet-in-the-middle 的思想。
具體來說,維護兩個陣列 ,,初始都只有 。為了防止算重,我們考慮將為了將質數分配成兩部分,分配給 和 ,將 中一個元素乘上 中一個元素組成答案。
具體如何分配呢?可以遍歷質數,如果 當前的大小小於 的大小,則分配給 ,否則分配給 。如程式碼:
std::vector<long long> a({1}), b({1});
for (size_t i = 0; primes[i] <= p && i < primes.size(); i++) {
int pi = primes[i];
if (a.size() > b.size()) std::swap(a, b);
auto as = a.size();
for (size_t j = 0; j < as; j++) {
long long tmp = a[j] * pi;
while (tmp <= n) {
a.emplace_back(tmp);
tmp *= pi;
}
}
}
std::sort(a.begin(), a.end());
std::sort(b.begin(), b.end());
對於極限資料,、 兩個陣列的大小一個是 ,一個是 。雙指標統計一下答案即可。
long long ans = 0;
for (size_t i = 0, j = b.size(); i < a.size(); i++) {
while (j > 0 && a[i] * b[j - 1] > n) j--;
ans += j;
}
法 2
定義 表示小於等於 的 -smooth number 的個數。小於等於 這個條件不好處理,可以改成有多少個 -smooth number 可以使得 。
由於 。所以可以得到轉移方程 ,其中 表示小於 的最大質數。容易寫出 dfs 程式碼:
constexpr std::array primes = {2, 3, 5, 7, 11, 13, 17, 19, 23,
29, 31, 37, 41, 43, 47, 53, 59, 61,
67, 71, 73, 79, 83, 89, 97};
long long dfs(long long x, int pi)
{
if (pi == 0) return std::__lg(x) + 1;
long long res = 0;
res += dfs(x, pi - 1);
if (x >= primes[p]) res += dfs(x / primes[p], p);
return res;
}
然而這樣是過不了的,極限資料下在我的電腦上跑了 28s。可以考慮使用記憶化,當然 的範圍很大,你沒法全部記憶化,可以記憶到 左右。
constexpr std::array primes = {2, 3, 5, 7, 11, 13, 17, 19, 23,
29, 31, 37, 41, 43, 47, 53, 59, 61,
67, 71, 73, 79, 83, 89, 97};
struct dfs
{
static constexpr int MAX_MEM = 0;
std::vector<std::vector<std::optional<long long>>> mem;
dfs(long long n, int p)
: mem(std::min((long long)MAX_MEM, n),
std::vector<std::optional<long long>>(p))
{
}
long long operator()(long long x, int p)
{
if (p == 0) return std::__lg(x) + 1;
if (x < (int)mem.size() && mem[x][p]) return mem[x][p].value();
long long res = 0;
res += operator()(x, p - 1);
if (x >= primes[p]) res += operator()(x / primes[p], p);
if (x < (int)mem.size()) mem[x][p] = res;
return res;
}
};