- 算法杂谈
你怎么知道我忘记线性筛怎么写了?
- @ 2026-5-4 18:20:07
当我们想要知道 区间内每个数的因数个数。
方法一:
我们直接遍历找出所有因数。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int f[N];
inline void F(int x)
{
f[1] = 1;
for (int i = 2; i <= x; i++)
{
int j = 1;
for (; j < i / j; j++) if (!(i % j)) f[i] += 2;
if (j * j == i) f[i]++;
}
return ;
}
signed main()
{
F(1000000);
return 0;
}
时间复杂度为 。
方法二:
我们先将其质因数分解,再用约数个数定理[1] ,计算出因数个数。
int f[N];
vector<pair<int, int> > p[N];
inline void F(int x)
{
f[1] = 1;
for (int i = 2; i <= x; i++)
{
int k = i;
for (int j = 2; j <= k / j; j++)
{
if (k == 1) break;
if (k % j) continue;
int lst = p[i].size();
p[i].push_back({j, 0});
while (!(k % j)) k /= j, p[i][lst].second++;
}
f[i] = 1 + (k > 1);
for (auto ite : p[i]) f[i] *= (ite.second + 1);
}
return ;
}
时间复杂度为 。
方法三:
在方法二的基础上,质因数分解只遍历质数。
int f[N];
vector<pair<int, int> > p[N];
vector<int> prime;
inline void F(int x)
{
f[1] = 1;
for (int i = 2; i <= x; i++)
{
int k = i;
for (auto j : prime)
{
if (k == 1 || j > k / j) break;
if (k % j) continue;
int lst = p[i].size();
p[i].push_back({j, 0});
while (!(k % j)) k /= j, p[i][lst].second++;
}
f[i] = 1 + (k > 1);
for (auto ite : p[i]) f[i] *= (ite.second + 1);
if (f[i] == 2) prime.push_back(i);
}
return ;
}
时间复杂度为 $\sum_2 ^n O(n \cdot \pi(\sqrt{n})) = O(\frac{n\sqrt{n}}{\ln n})$[2][3]。
方法四:
因为当 时 [4],所以我们可以依此来求出因数个数。
例如令 $n = {p_1}^{a_1} \times {p_2}^{a_2} \times \cdots {p_k}^{a_k}$,则我们可以找到其最小因数 ,然后不停将 直到其中没有因数 得到最终结果为 ,则 $x = {p_2}^{a_2} \times {p_3}^{a_3} \times \cdots {p_k}^{a_k}, \frac{n}{x} = {p_1}^{a_1}$。
显然 ,故 。
额外会有一些特殊情况需要去讨论。
int f[N];
inline void F(int x)
{
f[1] = 1;
for (int i = 2; i <= x; i++)
{
int k = i, cnt = 0;
for (int j = 2; j <= i / j; j++)
{
if (f[j] > 2) continue;
if (i % j) continue;
while (!(k % j)) k /= j, cnt++;
break;
}
if (k == i) f[i] = 2;
else if (k == 1) f[i] = cnt + 1;
else f[i] = f[k] * f[i / k];
}
return ;
}
时间复杂度为 。
方法五:
在方法四的基础上在遍历最小因数时遍历质数。
int f[N];
vector<int> prime;
inline void F(int x)
{
f[1] = 1;
for (int i = 2; i <= x; i++)
{
int k = i, cnt = 0;
for (auto j : prime)
{
if (j > i / j) break;
if (i % j) continue;
while (!(k % j)) k /= j, cnt++;
break;
}
if (k == i) f[i] = 2, prime.push_back(i);
else if (k == 1) f[i] = cnt + 1;
else f[i] = f[k] * f[i / k];
}
return ;
}
时间复杂度为 。
方法六:
用 Pollard-Rho[5] 单次 对每个数进行质因数分解,再用约数个数定理计算出因数个数。
using ll = long long;
int f[N];
inline ll mul(ll a, ll b, ll m)
{
ll r = 0;
a %= m; b %= m;
while (b)
{
if (b & 1) r = (r + a) % m;
a = (a + a) % m;
b >>= 1;
}
return r;
}
inline ll power(ll a, ll b, ll m)
{
ll r = 1; a %= m;
while (b)
{
if (b & 1) r = mul(r, a, m);
a = mul(a, a, m);
b >>= 1;
}
return r;
}
inline bool is_prime(ll n)
{
if (n < 2) return false;
if (n == 2 || n == 3) return true;
if (n % 2 == 0) return false;
ll d = n - 1, s = 0;
while (d % 2 == 0) d /= 2, s++;
for (ll a : {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37})
{
if (a >= n) continue;
ll x = power(a, d, n);
if (x == 1 || x == n - 1) continue;
bool composite = true;
for (ll r = 0; r < s; r++)
{
x = mul(x, x, n);
if (x == n - 1)
{
composite = false;
break;
}
}
if (composite) return false;
}
return true;
}
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
inline ll pollard_rho(ll n)
{
if (n % 2 == 0) return 2;
if (n % 3 == 0) return 3;
while (true)
{
ll c = uniform_int_distribution<ll>(1, n - 1)(rng);
auto f = [&](ll x) { return (mul(x, x, n) + c) % n; };
ll x = 2, y = 2, d = 1;
while (d == 1)
{
x = f(x);
y = f(f(y));
d = __gcd(abs(x - y), n);
}
if (d != n) return d;
}
}
inline void factorize(ll n, vector<ll>& factors)
{
if (n == 1) return;
if (is_prime(n))
{
factors.push_back(n);
return;
}
ll d = pollard_rho(n);
factorize(d, factors);
factorize(n / d, factors);
return ;
}
inline void F(int x)
{
f[1] = 1;
for (int i = 2; i <= x; i++)
{
if (is_prime(i))
{
f[i] = 2;
continue;
}
vector<ll> factors;
factorize(i, factors);
unordered_map<ll, int> cnt;
for (ll p : factors) cnt[p]++;
f[i] = 1;
for (auto &kv : cnt) f[i] *= (kv.second + 1);
}
return ;
}
时间复杂度为 [6]。
方法七:
在方法六的基础上,先线性筛一遍 的区间,然后将 is_prime() 函数改为单次 查询。
欸?线性筛……?你怎么知道我忘记线性筛怎么写了?[7]
using ll = long long;
int f[N];
bool isp[N];
vector<int> prime_table;
inline void linear_sieve(int n)
{
fill(isp, isp + n + 1, true);
isp[0] = isp[1] = false;
for (int i = 2; i <= n; i++)
{
if (isp[i]) prime_table.push_back(i);
for (int p : prime_table)
{
if (i * p > n) break;
isp[i * p] = false;
if (!(i % p)) break;
}
}
return ;
}
inline ll mul(ll a, ll b, ll m)
{
ll r = 0;
a %= m; b %= m;
while (b)
{
if (b & 1) r = (r + a) % m;
a = (a + a) % m;
b >>= 1;
}
return r;
}
inline ll power(ll a, ll b, ll m)
{
ll r = 1; a %= m;
while (b)
{
if (b & 1) r = mul(r, a, m);
a = mul(a, a, m);
b >>= 1;
}
return r;
}
inline bool is_prime(ll n)
{
return isp[n];
}
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
inline ll pollard_rho(ll n)
{
if (n % 2 == 0) return 2;
if (n % 3 == 0) return 3;
while (true) {
ll c = uniform_int_distribution<ll>(1, n - 1)(rng);
auto f = [&](ll x) { return (mul(x, x, n) + c) % n; };
ll x = 2, y = 2, d = 1;
while (d == 1)
{
x = f(x);
y = f(f(y));
d = __gcd(abs(x - y), n);
}
if (d != n) return d;
}
}
inline void factorize(ll n, vector<ll>& factors)
{
if (n == 1) return;
if (is_prime(n))
{
factors.push_back(n);
return;
}
ll d = pollard_rho(n);
factorize(d, factors);
factorize(n / d, factors);
return ;
}
inline void F(int x)
{
linear_sieve(x);
f[1] = 1;
for (int i = 2; i <= x; i++)
{
if (is_prime(i))
{
f[i] = 2;
continue;
}
vector<ll> factors;
factorize(i, factors);
unordered_map<ll, int> cnt;
for (ll p : factors) cnt[p]++;
f[i] = 1;
for (auto &kv : cnt) f[i] *= (kv.second + 1);
}
return ;
}
时间复杂度为 [8]。
方法八:
线性筛即可求出因数个数。
欸?线性筛……?你怎么知道我忘记线性筛怎么写了?
int f[N], e[N];
vector<int> prime;
inline void F(int x)
{
f[1] = 1;
for (int i = 2; i <= x; i++)
{
if (!f[i]) prime.push_back(i), f[i] = 2, e[i] = 1;
for (int p : prime)
{
if (i * p > x) break;
if (i % p) f[i * p] = f[i] << 1, e[i * p] = 1;
else
{
f[i * p] = f[i] / (e[i] + 1) * (e[i] + 2);
e[i * p] = e[i] + 1;
break;
}
}
}
return ;
}
时间复杂度为 。
0 条评论
目前还没有评论...