数学知识学习(持续更新)
数学知识学习(模板)
Ⅰ 质数
⒈ 试除法
⑴ 判定素数
时间复杂度:\(\Theta(\sqrt{n})\)
#include <bits/stdc++.h>
using namespace std;
bool isPrime(int x)
{
	if (x < 2)
		return false;
	for (int i = 2; i <= x / i; i ++ )
	{
		if (x % i == 0)
			return false;
	}
	return true;
}
int main()
{
	int n;
	cin >> n;
	while (n -- )
	{
		int x;
		cin >> x;
		if (isPrime(x))
			cout << "Yes" << endl;
		else
			cout << "No" << endl;
	}
	return 0;
}
⑵ 分解质因数
时间复杂度:\(\Theta(\log n)\)与\(\Theta(\sqrt{n})\)之间
#include <bits/stdc++.h>
using namespace std;
void isPrime(int x)
{
	for (int i = 2; i <= x / i; i ++ )
	{
		if (x % i == 0)
		{
			int s = 0;
			while (x % i == 0)
				x /= i, ++s;
			cout << i << " " << s << "\n";
		}
	}
	if (x > 1)
		cout << x << " " << 1 << "\n";
	cout << "\n";
}
int main()
{
	int n;
	cin >> n;
	while (n -- )
	{
		int x;
		cin >> x;
		isPrime(x);
	}
	return 0;
}
⒉ 埃氏筛
原理:
将 \(1\) ~ \(n\) 排成一行,从第1个开始,在每个数上向后把这个数的倍数全部筛掉,这样就可以只剩下质数了。
时间复杂度:\(\Theta(n\log \log n)\)
附:一般,\(\log \log n\) 会忽略不计,也就是说,时间复杂度近似 \(\Theta(n)\)。但是,真正能做到 \(\Theta(n)\) 的算法是下一个算法——线性筛。
Code - 模板
int primes[N], cnt;
bool st[N];
void ass(int n)//埃氏筛模板
{
	for (int i = 2; i <= n; ++i)
	{
		if (!st[i])
		{
			primes[cnt++] = n;
			for (int j = i + i; j <= n; j += i)
				st[j] = true;
		}
	}
}
Code - 用法
int main()
{
	int n;
	cin >> n;
	ass(n);
	cout << cnt;
	return 0;
}
记
我们发现这里面似乎会对某些数标记了很多次其为合数。有没有什么办法省掉无意义的步骤呢?请看下一个算法!
⒊ 线性筛法(埃氏筛优化)
优化方式:
我们在上一个算法中提到埃氏筛会对某些数标记了很多次其为合数,如果能让每个合数都只被标记一次,那么时间复杂度就可以降到 \(\Theta(n)\)了。
时间复杂度:\(\Theta(n)\)
Code - 模板
const int N = 1000010;
int primes[N], cnt;
bool st[N];
void xxs(int n)//线性筛模板
{
	for (int i = 2; i <= n; ++i)
	{
		if (!st[i])
			primes[cnt++] = i;
		for (int j = 0; primes[j] <= n / i; ++j)
		{
			st[primes[j]*i] = true;
			if (i % primes[j] == 0)
				break;
		}
	}
}
Code - 用法
int main()
{
	int n;
	cin >> n;
	xxs(n);
	cout << cnt;
	return 0;
}
分析
对于代码 if(i%primes[j]==0)break;,\(n\) 只会被最小的质因子筛掉。
证明如下:
这里,我们分两种情况来讨论。
- i%primes[j]==0,则- primes[j]一定是- i的最小质因子,- primes[j]也一定是- primes[j]*i的最小质因子。
- i%primes[j]!=0,则- primes[j]一定小于- i的所有质因子,- primes[j]也一定是- primes[j]*i的最小质因子。
证毕!
Ⅱ 约数
⒈ 试除法求约数
时间复杂度:\(\Theta(\sqrt{n})\)
Code - 模板
vector<int> get_divisors(int n)\\试除法求约数模板
{
	vector<int> res;
	for (int i = 1; i <= n / i; ++i)
	{
		if (n % i == 0)
		{
			res.push_back(i);
			if (i != n / i)
				res.push_back(n / i);
		}
	}
	sort(res.begin(), res.end());\\不必要时可以不加
	return res;
}
注意:这个模板求的约数是以 vector 的类型返回的,所以,在使用时,要小心。具体使用方法看下面的用法代码。
Code - 用法
int x;
cin >> x;
auto res = get_divisors(x);
for (auto t : res)
	cout << t << " ";
⒉ 约数个数与约数和专题
0 注
本小节的内容部分摘自这里
⑴ 基本定理
算术基本定理:设 \(n={p_1}^{r_1}{p_2}^{r_2}{p_3}^{r_3}\cdots{p_k}^{r_k}\)
约数个数公式:\(d(n)=(r_1+1)\times(r_2+1)\times(r_3+1)\times\cdots\times(r_k+1)\)
约数和公式:\(\sigma(n)=\prod\limits_{i=1}^k\left(\sum\limits_{j=0}^{r_i}{p_i}^{j}\right)\)
⑵ 求单个数的约数个数
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
/**
 * 功能:计算约数个数
 * @param n
 * @return
 */
LL getDivisorCount(LL x)
{
    unordered_map<int, int> primes; //key:质数 value:个数
    //求质数因子
    for (int i = 2; i <= x / i; i++)
        while (x % i == 0) x /= i, primes[i]++; //primes[i]表示质数i因子的个数+1
    //如果还有质数,那就加上
    if (x > 1) primes[x]++;
    //公式大法
    LL res = 1;
    for (auto p: primes) res = res * (p.second + 1);
    return res;
}
LL res;
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) cout << i << " " << getDivisorCount(i) << endl;
    return 0;
}
⑶ 求数字连乘积的约数个数
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
int main()
{
	int n;
	cin >> n;
	unordered_map<int, int> primes;
	while (n--)
	{
		int x;
		cin >> x;
		for (int i = 2; i <= x / i; ++i)
		{
			while (x % i == 0)
			{
				x /= i;
				++primes[i];
			}
		}
		if (x > 1)
			++primes[x];
	}
	long long res = 1;
	for (auto prime : primes)
		res = res * (prime.second + 1) % mod;
	cout << res;
}
⑷ 求单个数字的约数和
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
/**
 * 功能:计算约数之和
 * @param n
 * @return
 */
LL getSumOfDivisors(LL x)
{
    //拆出所有质数因子及质数因子个数
    unordered_map<int, int> primes;
    for (int i = 2; i <= x / i; i++)
        while (x % i == 0)
        {
            x /= i;
            primes[i]++;
        }
    if (x > 1) primes[x]++;
    //计算约数个数
    LL res = 1;
    for (auto p : primes)
    {
        LL a = p.first, b = p.second;
        LL t = 1;
        while (b--) t = (t * a + 1);
        res = res * t;
    }
    return res;
}
LL res;
int main()
{
    int n;
    cin >> n;
    cout<<getSumOfDivisors(n) << endl;
    return 0;
}
⑸ 求数字连乘积的约数和
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
unordered_map<int, int> primes;
int main()
{
	int n, x;
	cin >> n;
	while (n--)
	{
		cin >> x;
		for (int i = 2; i <= x / i; i++)
		{
			while (x % i == 0)
			{
				primes[i]++;
				x /= i;
			}
		}
		if (x > 1)
			primes[x]++;
	}
	ll res = 1;
	for (auto p : primes)
	{
		ll a = p.first, b = p.second;
		ll t = 1;
		while (b--)
			t = (t * a + 1) % mod;
		res = res * t % mod;
	}
	cout << res << endl;
	return 0;
}
⑹ 筛法求区间内的约数个数与约数和
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
/**
 * 功能:线性筛出约数个数与约数和
 * Tag:模板,约数个数,约数和
 */
const int N = 1e6 + 10;
int n;
int primes[N];      //质数数组
int idx;            //质数数组下标游标
bool st[N];         //是否已被筛出
int d[N];           //约数个数数组
int sigma[N];       //约数和数组
void get_divisor(int n)
{
    //积性函数的出发值
    d[1] = sigma[1] = 1;
    for (int i = 2; i <= n; i++)  //倍数
    {
        if (!st[i])primes[++idx] = i, d[i] = 2, sigma[i] = i + 1;
        for (int j = 1; i * primes[j] <= n & j <= idx; j++)
        {
            st[i * primes[j]] = true;
            d[i * primes[j]] = d[i] << 1;
            sigma[i * primes[j]] = sigma[i] * (primes[j] + 1);
            if (i % primes[j] == 0)
            {
                d[i * primes[j]] -= d[i / primes[j]];
                sigma[i * primes[j]] -= primes[j] * sigma[i / primes[j]];
                break;
            }
        }
    }
}
LL res;
int main()
{
    cin >> n;
    //开始筛约数个数,约数和
    get_divisor(n);
    //输出约数个数和
    for (int i = 1; i <= n; i++) res += d[i];
    cout << res << endl;
    return 0;
}
⑺辗转相除法求最大公约数
#include <bits/stdc++.h>
using namespace std;
int gcd(int a, int b)
{
	return b ? gcd(b, a % b) : a;
}
int main()
{
	int n;
	cin >> n;
	while (n -- )
	{
		int a, b;
		scanf("%d%d", &a, &b);
		printf("%d\n", gcd(a, b));
	}
	return 0;
}
⒊ 欧拉函数
定义
在数论,对正整数 \(n\),欧拉函数是小于等于 \(n\) 的正整数中与 \(n\) 互质的数的数目。
定理
设 \(n={p_1}^{r_1}{p_2}^{r_2}{p_3}^{r_3}\cdots{p_k}^{r_k}\)
那么 \(\varphi(n) = n\times\left(1 - \dfrac{1}{p_1}\right) \times \left(1 - \dfrac{1}{p_2}\right) \times\cdots\times\left(1 - \dfrac{1}{p_k}\right)\)
Code - 求单个数的欧拉函数
#include <bits/stdc++.h>
using namespace std;
int main()
{
	int n;
	cin >> n;
	while (n--)
	{
		int a;
		cin >> a;
		int res = a;
		for (int i = 2; i <= a / i; ++i)
		{
			if (a % i == 0)
			{
				res = res / i * (i - 1);
				while (a % i == 0)
					a /= i;
			}
		}
		if (a > 1)
			res = res / a * (a - 1);
		cout << res << endl;
	}
	return 0;
}
求 \(1\) 到 \(n\) 的欧拉函数之和(欧拉筛)
分析

Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1000005;
int n, phi[N], prime[N], cnt;
bool st[N];
long long get_euler(int x)
{
	phi[1] = 1;
	for (int i = 2; i <= x; i++)
	{
		if (!st[i])
		{
			prime[cnt++] = i;
			phi[i] = i - 1;
		}
		for (int j = 0; prime[j] <= x / i; j++)
		{
			st[i * prime[j]] = true;
			if (i % prime[j] == 0)
			{
				phi[i * prime[j]] = prime[j] * phi[i];
				break;
			}
			else
				phi[i * prime[j]] = (prime[j] - 1) * phi[i];
		}
	}
	long long res = 0;
	for (int i = 1; i <= x; i++)
		res += phi[i];
	return res;
}
int main()
{
	cin >> n;
	cout << get_euler(n) << endl;
	return 0;
}

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号