基础回顾--素数筛,欧拉函数
欧拉筛法:
直接判断素数耗时,换个角度标记非素数,那么没有被标记的就是素数。
关键点:
- 除2之外的偶数必然不是素数,所以只需要遍历奇数
- 寻找0-N范围中的素数时,遍历只需要从 2-->sqrt(N+0.5),因为sqrt(N+0.5)之后的数必然在第二层的循环中标记过。
- 二层循环的意思:一层循环找到了素数prime,则要将prime的所有倍数都筛去。注意从prime*prime开始筛,因<prime*prime的数必然能被之后找到的prime的倍数筛去。
const int maxn = 10004; bool vis[maxn]; int prime[maxn], cnt; void sort_prime() {
/* 细节注意,偶数的特例2是素数 */ prime[cnt++] = 2; int m = (int) sqrt(maxn + 0.5); for(int i=3; i<m; i+=2) { if(!vis[i]) { prime[cnt++] = i; for(int j=i*i; j<maxn; j+=i) { vis[j] = true; } } }
for(int i=(m&1)? m:m+1; i<maxn; i+=2)
if(!vis[i]) prime[cnt++] = i; return; }
欧拉函数:
求解所有<=n且与n互素的数的总数,特别的 phi(1) = 1。 (phi其实是数学符号的谐音~)
根据性质 phi(n) = n*(1-1/p1)*(1-1p2)...*(1-1/pk),pi是n的所有素因子。性质的公式进行变行--> phi(n) = n ∏ [ (pi-1)/pi ] --> 每一步看phi = n,phi = phi / pi * (pi-1)先除后乘防止溢出-->phi = phi - phi/pi -- > phi -= phi/pi。
int phi(int N) { int tmp = N, n = N; for(int i=0; i<cnt && prime[i]<=n; ++i) { if(n%prime[i] == 0) { tmp -= tmp/prime[i]; //欧拉函数性质推导出的式子 while(n%prime[i] == 0) // 去掉prime[i]因子 n /= prime[i]; } } //若n != 1,则必然还剩下一个素数没有用 if(n != 1) tmp -= tmp/n; return tmp; }