埃筛做法
首先什么是埃筛:
#include <stdio.h>
#include <string.h>
const int N = 100 + 8;
int isPrime[N];
void sieve() {
memset(isPrime, -1, sizeof isPrime);
isPrime[0] = isPrime[1] = 0;
for (int i = 2; i < N; ++i)
if (isPrime[i])
for (int j = 2; i * j < N; ++j) isPrime[i * j] = 0;
}
int main() {
sieve();
for (int i = 2; i < N; ++i)
if (isPrime[i]) printf("%d ", i);
return 0;
} 可以发现,在朴素的埃筛里,对于每一个合数,它会被每个它的质因数筛到。而如果是质数,他的质因数合并就是它本身。
所以只需要实时更新下每个数的质因数合并即可。
#include <bits/stdc++.h>
#define sc(x) scanf("%lld", &(x))
#define pr(x) printf("%lld\n", (x))
#define rep(i, l, r) for (int i = l; i <= r; ++i)
using namespace std;
typedef long long ll;
const int N = 4e6 + 7;
const int mod = 1e9 + 7;
bitset<N> isPrime;
ll ans[N];
int main() {
ll w[10] = {1};
rep(i, 1, 9) w[i] = w[i - 1] * 10;
ll n;
sc(n);
isPrime.set();
rep(i, 2, n) {
if (isPrime[i]) {
ans[i] = i;
int len = log10(i) + 1;
for (int j = 2; i * j <= n; ++j) {
int now = i * j;
isPrime[now] = 0;
do {
ans[i * j] = (ans[i * j] * w[len] % mod + i) % mod;
now /= i;
} while (now % i == 0);
}
}
}
ll res = 0;
rep(i, 2, n) res = (res + ans[i]) % mod;
pr(res);
return 0;
} 这种做法时间复杂度,本题300ms
欧拉筛 + DFS
欧拉筛是近似线性的筛法,这意味着不能像埃筛一样,对每个数更新答案。
但是可以从另一个角度出发:
- 对于每个质数,它的质因数合并就是它本身。
- 对于每个合数,它的质因数合并是它所有质因数的拼接。
- 反过来说,可以用所有的质因数乘出来所有的数,对于质因数合并,也就是拼接。
- 所以每个数和它的质因数合并都是一一对应的。
所以,可以直接去尝试拼接就可以了,如果是在所求范围内的,就加上权重即可,这也是唯一分解定理的逆向应用,非常巧妙。
#include <bits/stdc++.h>
#define sc(x) scanf("%lld", &(x))
#define pr(x) printf("%lld\n", (x))
#define rep(i, l, r) for (int i = l; i <= r; ++i)
using namespace std;
typedef long long ll;
const int N = 4e6 + 7;
const int mod = 1e9 + 7;
bitset<N> isPrime;
int prim[N + 10];
int pn = 0;
ll ans, n;
int w[10] = {1};
int pw[N];
void pre(int N) {
rep(i, 1, 9) w[i] = w[i - 1] * 10;
isPrime.set();
for (int i = 2; i <= N; i++) {
if (isPrime[i]) pw[pn] = w[int(log10(i) + 1)], prim[pn++] = i;
for (int j = 0; j < pn && i * prim[j] <= N; ++j) {
isPrime[i * prim[j]] = 0;
if (i % prim[j] == 0) break;
}
}
}
void dfs(int pos, ll key, ll val) {
ans = (ans + val) % mod;
for (int i = pos; i < pn; ++i) { // merge with bigger prime num
if (key * prim[i] > n) return;
dfs(i, key * prim[i], (val * pw[i] % mod + prim[i]) % mod);
}
}
int main() {
sc(n);
pre(n);
for (int i = 0; i < pn && prim[i] <= n; ++i)
// start dfs from each prime num
dfs(i, prim[i], prim[i]);
pr(ans);
return 0;
} 这种做法时间复杂度,本题70ms

京公网安备 11010502036488号