WELCOME

任何一个伟大的目标,都有一个微不足道的开始。

数学知识学习(持续更新)

 


本文与我的洛谷博客同步更新,在这里查看效果更佳。

数学知识学习(模板)

Ⅰ 质数

⒈ 试除法

⑴ 判定素数

时间复杂度:Θ(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; }

⑵ 分解质因数

时间复杂度:Θ(logn)Θ(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个开始,在每个数上向后把这个数的倍数全部筛掉,这样就可以只剩下质数了。

时间复杂度:Θ(nloglogn)

附:一般,loglogn 会忽略不计,也就是说,时间复杂度近似 Θ(n)。但是,真正能做到 Θ(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; }

我们发现这里面似乎会对某些数标记了很多次其为合数。有没有什么办法省掉无意义的步骤呢?请看下一个算法!

⒊ 线性筛法(埃氏筛优化)

优化方式:

我们在上一个算法中提到埃氏筛会对某些数标记了很多次其为合数,如果能让每个合数都只被标记一次,那么时间复杂度就可以降到 Θ(n)了。

时间复杂度:Θ(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 只会被最小的质因子筛掉。
证明如下:

这里,我们分两种情况来讨论。

  1. i%primes[j]==0,则 primes[j] 一定是 i 的最小质因子,primes[j] 也一定是 primes[j]*i 的最小质因子。
  2. i%primes[j]!=0,则 primes[j] 一定小于 i 的所有质因子,primes[j] 也一定是 primes[j]*i 的最小质因子。

证毕!

Ⅱ 约数

⒈ 试除法求约数

时间复杂度:Θ(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=p1r1p2r2p3r3pkrk

约数个数公式:d(n)=(r1+1)×(r2+1)×(r3+1)××(rk+1)

约数和公式:σ(n)=i=1k(j=0ripij)

⑵ 求单个数的约数个数

#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=p1r1p2r2p3r3pkrk

那么 φ(n)=n×(11p1)×(11p2)××(11pk)

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; }

1n 的欧拉函数之和(欧拉筛)

分析

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; }

__EOF__

本文作者绿树公司
本文链接https://www.cnblogs.com/wp-lvshu/p/15802232.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   绿树公司  阅读(55)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示