数论 _ 质数和约数
质数
判定质数
试除法判断素数
复杂度: (每次一定是 )
原理: 约数成对出现(完全平方数除外)
代码:
bool is_prime(int x) { if (x < 2) return false; for (int i = 2; i <= x / i; i ++ ) if (x % i == 0) return false; return true; }
注意点:
for循环的条件建议是: i<= x / i;
因为
i*i <= x
可能i*i会爆int(long long)i<=sqrt(x)
每次调用sqrt函数可能会超时。
分解质因数 | 求解最大质因数
暴力写法:
就是枚举从2到x的每一个数,如果是x的因子,那么就除去他。
需要注意的是:
因为一个合数的质因子一定在扫描到这个合数之前就从x中剔除掉了,所以无需验证 i 是否为质数
代码:
void divide(int x) { for (int i = 2; i <= x; i ++ ) if (x % i == 0) { int s = 0; while (x % i == 0) x /= i, s ++ ; cout << i << ' ' << s << endl; } }
然后有条性质:
n的因子中,最多只包含一个大于sqrt (n) 的质因子。
证明: 如果有两个,那么这两个质数相乘就大于n了。
所以我们可以将代码改为:
暴力2到 ,然后单独处理大于的那一个。
模板代码
上限复杂度:
下限复杂度:
代码:
void divide(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 << endl; } if (x > 1) cout << x << ' ' << 1 << endl; cout << endl; }
筛质数
欧拉线性质数筛
原理
基本思想 :
在埃氏筛法的基础上,让每个合数只被它的 最小质因子×另一个数 筛选一次,以达到不重复的目的。
关键:
if(i%prime[j]==0)break;
理解:
- 当 i是prime[j]的倍数时,
- 如果继续运算 j+1,就会筛掉
- 当时,还会运行到
- 就重复筛了,所以跳出循环,保证每个数只筛选一次。
比如说:
对于筛选90=5×3×3×2=45×2。我们目的是保证90只被他的最小质因子2筛选一次。
当i=30=5×3×2时,因为30的最小质因数是2,
所以当质数为3时、筛选的数是30×3=90,
而对于30×3:因为 30的最小质因数是2 所以一定可以找到30×3=另一个数×2。
所以当i%prime[j]==0时,需要结束掉。
代码
int primes[N], top=0;//存储素数 bool st[N];//记录是否为素数 void get_prime(int n) { st[1] = true; for (int i = 2; i <= n; i++) { if (!st[i]) primes[top++] = i; for (int j = 0; j *i <=n && j < top; j++) { st[i * primes[j]] = true; if (i % primes[j]==0)//关键 break; } } }
代码可以再简单一下把j < top这个条件去掉。
int primes[N], top=0;//存储素数 bool st[N];//记录是否为素数 void get_prime(int n) { st[1] = true; for (int i = 2; i <= n; i++) { if (!st[i]) primes[top++] = i; for (int j = 0; j *i <=n ; j++) { st[i * primes[j]] = true; if (i % primes[j]==0)//关键 break; } } }
素数定理
[1,N]中素数的个数约为NlgN。
则从[1,N]中人选一个数,其为质数的概率为1/lgN。
算数基本定理
任意一个整数都能被分解为如下形式:
。 其中p为质数。
约数
小知识:
int 范围内约数最多的数的个数为 1536,那个数为 1745944200
求所有约数
试除法求约数
和求质因子差不多,直接看代码
复杂度:
代码:
vector<int> get_divisors(int x) { vector<int> res; for (int i = 1; i <= x / i; i ++ ) if (x % i == 0) { res.push_back(i); if (i != x / i) res.push_back(x / i); } sort(res.begin(), res.end()); return res; }
约数个数
问题:求一个数的约数个数
前置知识:算数基本定理
任意一个整数都能被分解为如下形式:
。 其中p为质数。
如果n被分解成上面的形式,由乘法原理,得约数个数就是
应用:求n个数乘积的约数个数
给定 n 个正整数 ai,请你输出这些数的乘积的约数个数
思路:
分别求出每一个的,然后把每一个数的指数累加即可。
代码:
#include <iostream> #include <algorithm> #include <unordered_map> #include <vector> using namespace std; typedef long long LL; const int N = 110, 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] ++ ; } LL res = 1; for (auto p : primes) res = res * (p.second + 1) % mod; cout << res << endl; return 0; }
约数之和
问题:求一个数的约数之和
前置知识:算数基本定理
任意一个整数都能被分解为如下形式:
。 其中p为质数。
如果n被分解成上面的形式,约数之和显然是
应用:求n个数乘积的约数之和
给定n个正整数,请你输出这些数的乘积的约数之和
思路:
分别求出每一个的,然后最终集中带入公式即可。
代码:
#include <iostream> #include <algorithm> #include <unordered_map> #include <vector> using namespace std; typedef long long LL; const int N = 110, 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] ++ ; } 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; }
求是X的约数的个数(数的值可重复)
题意: 共有 N 个整数 A1,A2,…,AN,对于每一个数 Ai,求其他的数中有多少个是它的约数。
思路: 对于每个数判断其他数是不是他的倍数。
1291. 轻拍牛头
#include <bits/stdc++.h> #define endl '\n' #define int long long using namespace std; typedef pair<int, int> pii; const int N = 1e6 + 10; const int INF = 0x3f3f3f3f3f; int n; int a[N]; int cnt[N],st[N]; void slove() { cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; st[a[i]]++; } for(int i=1;i<=N;i++){ if(!st[i]) continue; cnt[i]+=st[i]-1; for(int j=i+i;j<=N;j+=i){ if(st[j]) cnt[j]+=st[i]; } } for(int i=1;i<=n;i++){ cout<<cnt[a[i]]<<endl; } } signed main() { // int t; // cin >> t; // while (t--) slove(); return 0; }
求n!的约数个数
1294. 樱花
题意
通过化简可以得到题意是求: 的约数个数
思路:
看代码吧
#include <iostream> #include <cstring> #include <cstdio> using namespace std; #define int long long const int N =1e6+10; int mod=1e9+7; int n; int primes[N],cnt; bool st[N]; void getprimes(){ for(int i=2;i<=n;i++){ if(!st[i]) primes[cnt++]=i; for(int j=0;primes[j]*i<=n;j++){ st[primes[j]*i]=true; if(i%primes[j]==0) break; } } } signed main() { cin>>n; int ans=1; getprimes(); // cout<<ans<<endl; for(int i=0;i<cnt;i++){ int p=primes[i]; int s=0; for(int j=n;j;j/=p) s+=j/p;//求n!的约数个数 ans=(ans*(s*2+1)%mod)%mod;//求n!的平方的约数的个数 } cout<<ans<<endl; return 0; }
198. 反素数
代码:
#include <bits/stdc++.h> #define endl '\n' #define int long long using namespace std; typedef pair<int, int> pii; const int N = 1e6 + 10; const int INF = 0x3f3f3f3f3f; int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23}; int n; int maxd, number; //u: 当前的质因子 //last:上一个质因子的次方数(也就是这次dfs次方的最大值) //p:这次dfs之前的 因子的乘积 //s:约数的个数 void dfs(int u, int last, int p, int s) { int k = primes[u]; if (s > maxd || s == maxd && p < number) { maxd = s; number = p; } if (u == 9) return; for (int i = 1; i <= last; i++) { if (p * primes[u] > n) break; p *= primes[u]; dfs(u + 1, i, p, s * (i + 1)); //s * (i + 1)是因为求的是所有的约数,比如 //对于18,(3,9),(2,6,18)...都是约数 } } void slove() { cin >> n; dfs(0, 30, 1, 1); cout << number << endl; } signed main() { // int t; // cin >> t; // while (t--) slove(); return 0; }
本文作者:kingwzun
本文链接:https://www.cnblogs.com/kingwz/p/16503763.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步