质数筛 + 欧拉函数

质数

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;
posted @ 2021-11-04 21:05  Yra  阅读(76)  评论(0)    收藏  举报