质数筛 + 欧拉函数
质数
1、试除法
For(int i = 1, i ≤ sqrt(x); i++)
2、埃氏筛(优化版)
求N以内的质数:
对于每个数x,只需从x²开始,将x²,(x+1)x, (x+2)x, ... ,[N/x]*x标记为合数即可;
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define For(i,a,b) for (int i=(a);i<=(b);++i)
#define Fod(i,b,a) for (int i=(b);i>=(a);--i)
#define mls multiset
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define itt iterator
#define clr(x) memset(x, 0, sizeof(x));
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
int n;
bool is_prime[100005];//1为合数,0为质数
int main ()
{
cin >> n;
For(i, 2, n)
{
if(is_prime[i]) continue;
printf("%d ", i); //i是质数;
For(j, i, n/i) // i*i,i*(i+1),i*(i+2)...,i*(n/i)
is_prime[j*i] = 1;
}
return 0;
}
3、线性筛(欧拉筛)
因为埃氏筛在筛的过程中,每个被筛掉的数会被筛好几次,以至于提升了复杂度为O(nloglogn),而线性筛则能避免每个数被重复筛的现象,将复杂度压缩到了线性O(n)
思路:我们用isprime[] 用于判断某个数是否未知数,prime[]用来存放质数,对于每个合数我们将其分解为其最大因子 \(*\) 最小质因数的形式,而每次筛,只用最小质因数来筛,避免重复。
比如 \(6 = 2 * 3\) ,对于6我们只会用2去筛,而3则会跳过。 那么我们是如何实现这一操作的呢?
这就是欧拉线性筛的精髓所在 if(i % prime[j] == 0) break;
代码示例
//#pragma comment(linker, "/STACK:10240000000000,10240000000000")
//#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define For(i,a,b) for (int i=(a);i<=(b);++i)
#define Fod(i,b,a) for (int i=(b);i>=(a);--i)
#define mls multiset
#define lb lower_bound
#define ub upper_bound
#define pb push_back
#define pob pop_back
#define itt iterator
#define lowbit(x) x & (-x)
#define clr(x) memset(x, 0, sizeof(x));
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int MAXN = 0x7fffffff;
const int MOD = 1000000007;
int n;
int prime[100005];
bool isprime[100005];
int main ()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
isprime[1] = 1;
int cnt = 0;
For(i, 2, n)
{
if(!isprime[i]) prime[++cnt] = i; //将质数加入prime数组里
for(int j = 1; j <= cnt && i * prime[j] <= n; j++) 进行筛的操作
{
isprime[i * prime[j]] = 1; //i可以理解为是每个合数的最大因子,而prime[j]则是最小质因数
if(i % prime[j] == 0) break; //线性筛的精髓,将时间复杂度压缩成了O(n)
}
}
For(i, 1, cnt) cout << prime[i] << " ";
return 0;
}
首先我们筛除每个合数的条件是,通过每个数的最小质因数 与 最大因子的乘积来进行筛除,下面简称筛条件
为什么要加入 if(i % prime[j] == 0) break;呢?
倘若继续执行下去,对于合数 i * prime[j + 1] 此时其最小质因数并不是prime[j + 1],而是prime[j],因为i能被prime[j]整除,因此i里面一定包含prime[j],所以此时将不满足筛条件。
欧拉函数
1、欧拉函数的定义:
\(\phi(n)\) 表示小于等于n的正整数中与n互质的数的个数。
2、欧拉函数计算公式
\(\phi(n) = \prod_i^r(1-\frac{1}{p^i}),其中n=\prod_i^rp_i^{k_i}\)
3、欧拉函数是积性函数,因此\(\phi(ab) = \phi(a)*\phi(b)\)
4、欧拉定理:若\(gcd(a,m)=1\),则\(a^{\phi^{(m)}} = 1(mod\;m)\)
特别地,当\(\phi(n)\)为质数时,即为费马小定理 \(a^p=a(mod\;p)\)
求单个数的欧拉函数
int t;
cin >> t;
while(t--)
{
int n;
cin >> n;
int res = n;
for(int i = 2; i*i <= n; i++)
{
if(n % i == 0)
{
res = res / i * (i - 1);
while(n % i == 0)
n /= i;
}
}
if(n > 1) res = res / n * (n - 1);
cout << res << endl;
}
埃氏筛求欧拉函数
int n;
cin >> n;
int phi[100005];
For(i, 1, n) phi[i] = i;
For(i, 2, n)
{
if(phi[i] == i)
{
for(int j = i; j <= n; j += i)
{
phi[j] = phi[j] / i * (i - 1);
}
}
}
For(i, 1, n) cout << phi[i] << " ";
cout << endl;
线性欧拉筛求欧拉函数
int n;
int prime[100005];
int phi[100005];
int cnt = 0;
bool isprime[100005];
cin >> n;
phi[1] = 1;
for(int i = 2; i <= n; i++)
{
if(!isprime[i]) prime[++cnt] = i, phi[i] = i - 1;
for(int j = 1; j <= cnt && i * prime[j] <= n; j++)
{
isprime[i * prime[j]] = 1;
if(i % prime[j] == 0)
{
phi[i * prime[j]] = phi[i] * prime[j];//i中包含prime[j]∴phi[i]包含(1 - 1/prime[j]),就不用乘了
break;
}
phi[i * prime[j]] = phi[i] * phi[prime[j]]; //积性函数性质
}
}
For(i, 1, n) cout << phi[i] << " ";
cout << endl;

浙公网安备 33010602011771号