ACM-ICPC 2018 南京赛区网络预赛 J. Sum
欧拉筛
欧拉筛可以在线性的时间内筛素数。原理是每一个合数都被它的最小质因子筛去。不会多筛,也不会漏筛。
代码中一般有两个数组,
- 标记是否是素数的数组 v i s [ ] vis[] vis[]
- 保存素数的数组
p
r
i
m
e
[
]
prime[]
prime[]
int vis[N], prime[N]; int cnt; void isprime(){ cnt = 0; for (int i = 2; i <= n; i++){ if(!vis[i]){ // 存素数 prime[cnt++] = i; } for (int j = 0; j < cnt && prime[j] * i <= n; j++){ vis[prime[j] * i] = 1; if(i % prime[j] == 0) // 关键。保证了只被最小质因子筛去 break; } } }
从代码中可以看到,外部的 i 是作为倍数使用,每次用素数更新合数时,都是在当前倍数 i i i 下,乘以整个素数表 p r i m e [ ] prime[] prime[]。
打个表看一下。
注意看为什么 12 不被 3 X 4 更新。
i 素数表 所更新的合数
2 2 4
3 2 3 6 9
4 2 3 8 --->// 不会更新 12, 因为 4 % 2 == 0,之后就 break了,
5 2 3 5 10 15 25 // 而 12 应该被最小质因子 2 更新
6 2 3 5 12
7 2 3 5 7 14 21 35 49
8 2 3 5 7 16
9 2 3 5 7 18 27
---------------------------------------
当 i % prime [j] == 0 时,说明 i = k * prime[j]。
再往上更新时,要跟新的值 num = i * prime [j+1]。
这时发生了重复,因为 num = k * prime [j] * prime [j+1] = n * prime [j]
也就是 num 会被 prime[j] 乘以一个 n 数给更新。
这样就保证了被最小质因子更新
应用
题意
定义 f ( n ) f(n) f(n) 表示整数 n n n 能拆成几对 n = a ∗ b n = a * b n=a∗b。其中要满足 a , b a, b a,b 没有平方数的因子。
求 ∑ i = 1 n f ( i ) \sum_{i=1}^{n} f(i) ∑i=1nf(i), ( n ≤ 2 ⋅ 1 0 7 ) \left(n \leq 2 \cdot 10^{7}\right) (n≤2⋅107)
分析
因为 n = a ∗ b n = a * b n=a∗b,且 a , b a, b a,b 没有平方因子,即: a , b a, b a,b分解质因子之后没有重复的。那么 n n n 的相同质因子个数不能超过 2
对质因子个数进行分类讨论,算出
f
(
i
)
f(i)
f(i)即可。
设
x
=
i
∗
p
r
i
m
e
[
j
]
x = i * prime[j]
x=i∗prime[j]
- 数 i 是素数 。那么 f ( i ) f(i) f(i) = 2,因为只有两种 1 ∗ i , i ∗ 1 1 * i,i * 1 1∗i,i∗1。
- 数 i 不是素数,还要再分两种
-
那 i % prime[j] == 0 之前的
这时相当与再数字 i 的分配方式下面多了一个质数,可以放在左边,也可以放再右边。例如 i = 9 时,prime[j] = 2.
原本 9 = 3 * 3。现在多了一个 2,放在两边都可以,即 f [ x ] = f [ i ] ∗ 2 f[x] = f[i] * 2 f[x]=f[i]∗2 -
当 i % prime[j] == 0,还要分两种
当 i 里有两个或以上的 prime[j] 时,例 i = 4, prime[j] = 2 时。这时候将prime[j]放两边都不行,都会产生平方数。因此 f [ x ] = 0 f[x] = 0 f[x]=0当 i 里有一个prime[j]时,例 i = 6,prime[j] = 2 时,这时,prime[j] 只能放在一边,相当于没有了 prime[j] 的影响。 f [ x ] = f [ i ] / 2 f[x] = f[i] / 2 f[x]=f[i]/2
-
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 2e7 + 5;
int _, n, cnt;
bool vis[N];
int prime[N];
int ans[N];
void isprime(){
cnt = 0;
ans[1] = 1;
for (int i = 2; i < N; i++){
if(!vis[i]){
prime[cnt++] = i;
ans[i] = 2;
}
for (int j = 0; j < cnt && prime[j] * i < N; j++){
int x = prime[j] * i;
vis[x] = 1;
if(i % prime[j] == 0){ // prime[j] 是 i 的最小质因子
if(i % (prime[j] * prime[j]) == 0)
ans[x] = 0;
else
ans[x] = ans[i] / 2;
break;
}else{
ans[x] = ans[i] * 2;
}
}
}
for (int i = 1; i < N; i++){
ans[i] += ans[i - 1];
}
}
int main(){
isprime();
for (scanf("%d", &_); _; _--){
scanf("%d", &n);
printf("%d\n", ans[n]);
}
return 0;
}