【数学专题】约数个数与欧拉函数
整理的算法模板合集: ACM模板
目录
- 一、约数个数
- 1. AcWing 1291. 轻拍牛头
- 2. AcWing 1294. 樱花
- 2.1 AcWing 197. 阶乘分解
- 3. AcWing 198. 反素数
- 4. AcWing 200. Hankson的趣味题
- 二、欧拉函数
- 1. AcWing 201. 可见的点
- 2. AcWing 220. 最大公约数
题目 | 算法 |
---|---|
AcWing 1291. 轻拍牛头 | 求约数的个数 |
AcWing 1294. 樱花 | 求(n!)2(n!)^2(n!)2的约数的个数 |
AcWing 198. 反素数 | 通过分析元素性质发现可以直接dfs |
一、约数个数
本节前半部分需要用到的知识:
由算数基本定理得正整数N可以写作N=p1C1×p2C2×p3C3⋯×pmCmN=p_1^{C_1}\times p_2^{C_2} \times p_3^{C_3} \cdots \times p_m^{C_m}N=p1C1×p2C2×p3C3⋯×pmCm
N的正约数个数为(ΠΠΠ是连乘积的符号,类似∑∑∑)
(c1+1)×(c2+1)×⋯(cm+1)=Πi=1m(ci+1)(c_1+1)\times (c_2+1)\times \cdots (c_m+1)=\Pi_{i=1}^{m}(ci+1)(c1+1)×(c2+1)×⋯(cm+1)=Πi=1m(ci+1)
N的所有正约数和为
(1+p1+p12+⋯+p1c1)×⋯×(1+pm+pm2+⋯+pmcm)=∏i=1m(∑j=0ci(pi)j)(1+p_1+p_1^2+\cdots +p_1^{c_1})\times\cdots\times(1+p_m+p_m^2+\cdots +p_m^{c_m})=\prod_{i=1}^{m}(\sum_{j=0}^{c_i}(p_i)^j)(1+p1+p12+⋯+p1c1)×⋯×(1+pm+pm2+⋯+pmcm)=i=1∏m(j=0∑ci(pi)j)
1. AcWing 1291. 轻拍牛头
因为
如果我们分别求每个数的约数的个数,那么每次的时间复杂度至少是O(n)O(\sqrt n)O(n)的,所以我们没必要每次都一个一个求,我们正着直接求很难,那么我们就倒着求,我们类比线性筛的思路,如果对于一个数x而言,数d是x的约数,那么x一定是d的倍数,所以我们直接筛一遍倍数即可。时间复杂度为O(nlogn)O(nlogn)O(nlogn)
注意最后需要除去自己
#include<cstdio>
#include<algorithm>
#include<cstring>using namespace std;const int N = 1000007;int n, m;
int a[N];
int primes[N];
int cnt[N];
int S[N];int main()
{scanf("%d", &n);for(int i = 1; i <= n; ++ i){scanf("%d", &a[i]);cnt[a[i]] ++ ;}for(int i = 1; i <= 1e6; ++ i){//nlognfor(int j = i; j <= 1e6; j += i){S[j] += cnt[i];}}for(int i = 1; i <= n; ++ i)printf("%d\n", S[a[i]] - 1);//除去它自己return 0;
}
2. AcWing 1294. 樱花
所以本题实际上转换成了求n!2n!^2n!2的约数的个数,我们根据公式,
由算数基本定理得正整数N可以写作N=p1C1×p2C2×p3C3⋯×pmCmN=p_1^{C_1}\times p_2^{C_2} \times p_3^{C_3} \cdots \times p_m^{C_m}N=p1C1×p2C2×p3C3⋯×pmCm
N的正约数个数为(ΠΠΠ是连乘积的符号,类似∑∑∑)
(c1+1)×(c2+1)×⋯(cm+1)=Πi=1m(ci+1)(c_1+1)\times (c_2+1)\times \cdots (c_m+1)=\Pi_{i=1}^{m}(ci+1)(c1+1)×(c2+1)×⋯(cm+1)=Πi=1m(ci+1)
求一个数的约数个数是分解质因数连乘,那么求一个数的阶乘的约数的个数实际上就是求1~n的每个数的约数个数的乘积。
因为n!2n!^2n!2太大了,所以我们引入一道题目:我们发现只需要将阶乘分解之后,所有质因子的指数的乘积的二倍既是答案。
2.1 AcWing 197. 阶乘分解
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>using namespace std;const int N = 1000010;int primes[N], cnt;
bool st[N];void init(int n)
{for (int i = 2; i <= n; i ++ ){if (!st[i]) primes[cnt ++ ] = i;for (int j = 0; primes[j] * i <= n; j ++ ){st[i * primes[j]] = true;if (i % primes[j] == 0) break;}}
}int main()
{int n;cin >> n;init(n);for (int i = 0; i < cnt; i ++ ){int p = primes[i];int s = 0;for (int j = n; j; j /= p) s += j / p;printf("%d %d\n", p, s);}return 0;
}
所以我们只需要在上述代码中稍作修改,累积的是每个质因子的指数的二倍即可。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>using namespace std;const int N = 1000007;
typedef long long ll;
ll mod = 1e9 + 7;ll n, m, cnt;
int primes[N];
bool vis[N];void get_primes(ll n)
{vis[0] = vis[1] = 1;for(int i = 2; i <= n; ++ i){if(vis[i] == 0){primes[++ cnt] = i;}for(int j = 1; j <= cnt && i * primes[j] <= n; ++ j){vis[i * primes[j]] = true;if(i % primes[j] == 0)break;}}
}int main(){scanf("%lld", &n);get_primes(n);ll res = 1;for(int i = 1; i <= cnt; ++ i){int p = primes[i];int sum = 0;for(int j = n; j; j /= p)sum += j / p;res = (res * (2 * sum + 1)) % mod;}cout << res % mod << endl;return 0;
}
3. AcWing 198. 反素数
很重要的一个解决问题的方法:找题目的性质!!!
如果没有思路的话,或者说数论的题目,所给的条件太少了,我们可以找一下题目中各各变量的性质!
所以我们直接爆搜即可。然后搜的过程中应用那三个我们发现的性质,其中这三个性质在之后的题目中用处也是非常大的。
//我们只用到了最多9个质因子,因为再多乘起来就大于1e9了,所以我们直接打表找到前9个质因子即可。
//我们需要找到的是约数个数最多的最小的数
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>using namespace std;const int N = 500007;
typedef long long ll;
int primes[9] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
int n, m;
int maxnum, number;
//使用的是第几个质数,最大次数,乘起来得到的x(反素数备选项),当前约数的个数(我们要找的是约数个数最多的最小的数)
void dfs(int cnt, int last_pow, int mul, int num)
{if(num > maxnum || (num == maxnum && mul < number)){maxnum = num;number = mul;}if(cnt == 9)return ;for(int i = 1; i <= last_pow; ++ i){//当前质因子的次数if((ll)primes[cnt] * mul > n) break;mul *= primes[cnt];//这个是要修改的dfs(cnt + 1, i, mul, num * (i + 1));//约数个数就是Π(ci + 1)}
}int main()
{scanf("%d", &n);dfs(0, 30, 1, 1);cout << number << endl;return 0;
}
4. AcWing 200. Hankson的趣味题
我的标程(能过原题):
但是y总竟然造了一组数据把标程给卡T了…
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>using namespace std;const int N = 50007;
int n, m, t, ans;
int a, b, c, d;int read()
{int x = 0, f = 1;char ch = getchar();while(ch > '9' || ch < '0'){if(ch == '-')f = -1; ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = getchar();}return x * f;
}int gcd(int a, int b)
{if(b == 0)return a;return gcd(b, a % b);
}int main()
{t = read();while(t -- ){ans = 0;a = read(), b = read(), c = read(), d = read();for(int x = 1, y; x * x <= d; ++ x){if(d % x == 0){if(gcd(x, a) == b && d * gcd(x, c) == x * c){ans ++ ;}if(x != d / x){y = d / x;if(gcd(y, a) == b && d * gcd(y, c) == y * c){ans ++ ;}}}}printf("%d\n", ans);}return 0;
}
%%%
#include <iostream>
#include <algorithm>
#include <vector>using namespace std;typedef long long LL;
typedef pair<int, int> PII;const int N = 45000, M = 50;int primes[N], cnt;
bool st[N];PII factor[M];
int cntf;int divider[N], cntd;void get_primes(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;}}
}int gcd(int a, int b)
{return b ? gcd(b, a % b) : a;
}void dfs(int u, int p)
{if (u > cntf){divider[cntd ++ ] = p;return;}for (int i = 0; i <= factor[u].second; i ++ ){dfs(u + 1, p);p *= factor[u].first;}
}int main()
{get_primes(N);int n;scanf("%d", &n);while (n -- ){int a0, a1, b0, b1;scanf("%d%d%d%d", &a0, &a1, &b0, &b1);int d = b1;cntf = 0;for (int i = 0; primes[i] <= d / primes[i]; i ++ ){int p = primes[i];if (d % p == 0){int s = 0;while (d % p == 0) s ++, d /= p;factor[ ++ cntf] = {p, s};}}if (d > 1) factor[ ++ cntf] = {d, 1};cntd = 0;dfs(1, 1);int res = 0;for (int i = 0; i < cntd; i ++ ){int x = divider[i];if (gcd(x, a0) == a1 && (LL)x * b0 / gcd(x, b0) == b1){res ++ ;}}printf("%d\n", res);}return 0;
}
二、欧拉函数
线性筛筛欧拉函数的原理
1. AcWing 201. 可见的点
用线性筛筛欧拉函数即可。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<iostream>using namespace std;
typedef long long ll;
const int N = 500007, M = 1000007, INF = 0x3f3f3f3f;
const double inf = 1e100;
const double eps = 1e-8;
const int mod = 10007;int primes[N], cnt;
int phi[N];
bool vis[N];void init(int n)
{phi[1] = 1;for(int i = 2; i <= n; ++ i){if(!vis[i]){primes[ ++ cnt] = i;phi[i] = i - 1;}for(int j = 1; j <= cnt && i * primes[j] <= n; ++ j){vis[i * primes[j]] = true;if(i % primes[j] == 0){//最小的质因子phi[i * primes[j]] = phi[i] * primes[j];break;}else phi[i *primes[j]] = phi[i] * (primes[j] - 1);}}}int n, m, t, kcase;int main()
{scanf("%d", &t);init(N - 1);while(t -- ){scanf("%d", &n);ll ans = 0;for(int i = 1; i <= n; ++ i)ans += phi[i] * 2;printf("%d %d %lld\n", ++ kcase, n, ans + 1);}return 0;
}
2. AcWing 220. 最大公约数
我们可以莫比乌斯反演!
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>using namespace std;
typedef long long ll;
const int N = 10000007;int n, m;
int primes[N];
int phi[N];
int cnt;
bool vis[N];
ll sum[N];void init(int n)
{phi[1] = 0;//这里应该是0for(int i = 2; i <= n; ++ i){if(vis[i] == 0){primes[ ++ cnt] = i;phi[i] = i - 1;}for(int j = 1; j <= cnt && primes[j] * i <= n; ++ j){vis[i * primes[j]] = true;if(i % primes[j] == 0){phi[i * primes[j]] = phi[i] * primes[j];break;}phi[i * primes[j]] = phi[i] * (primes[j] - 1);}}for(int i = 1; i <= n; ++ i)//求phi[n]的前缀和sum[i] = sum[i - 1] + phi[i];
}int main()
{scanf("%d", &n);init(n);ll ans = 0;for(int i = 1; i <= cnt; ++ i){int p = primes[i];ans += sum[n / p] * 2 + 1;}printf("%lld\n", ans);return 0;
}
- P2398 GCD SUM
- P4139 上帝与集合的正确用法
【数学专题】约数个数与欧拉函数相关推荐
- 1~n中与n互质数的个数(欧拉函数)
对于1~n中所有跟n互质的数的个数问题,我们会用欧拉函数来解决. 在开始讲欧拉函数之前,需要先了解质因数分解,传送门:http://t.csdn.cn/tqzvO.ok现在正式开始欧拉函数的推导. 以 ...
- 互质数的个数(欧拉函数)C/C++
欧拉函数 O(n)=n(1-1/P1)(1-1/P2)-(1-1/Pn) ,其中P1-Pn为n的质因子,求出来的结果就是题目所求. 不知道为社么这么写时间超限,下面那种方式写就能过. #include ...
- 【数论】【Polya定理】【枚举约数】【欧拉函数】【Java】poj2154 Color
你随便写一下出来,发现polya原理的式子里面好多gcd是相同的,gcd(n,i)=k可以改写成gcd(n/k,i/k)=1,也就是说指数为k的项的个数为phi(n/k),就很好求了,最后除的那个n直 ...
- 数学/数论专题:莫比乌斯函数与欧拉函数
数学/数论专题:莫比乌斯函数与欧拉函数(进阶) 0. 前言 1. 前置知识 2. 正文 3. 总结 4. 参考资料 0. 前言 本篇文章会从狄利克雷卷积的角度,讨论莫比乌斯函数与欧拉函数的相关性质. ...
- 数学知识——欧拉函数
1. 欧拉函数 定义:欧拉函数ψ(n) 表示1~n中与n互质的数的个数 公式:如果一个数可以被分解质因式为N = p1α1 *p2α2--pkαk 则ψ(n) = n(1 - 1/p1)(1 - 1/ ...
- 欧拉函数——数学知识(c++)
定义:欧拉函数表示1-N中与N互质的数的个数: 给定一个数n,求在[1,n]这个范围内两两互质的数的个数 对于这个范围内的每一个数,我们只要找到不超过这个数且与这个数互质的数的个数就可以了 欧拉函数用 ...
- 数论 —— 欧拉函数
[定义] 对正整数 n,欧拉函数是小于等于 n 的数中与 n 互质的数的个数,记作: 例如:,因为 1.3.5.7 均与 8 互质. [性质] 1)若 n 为一素数 p,则: 2)若 n 为一素数 p ...
- 算法 {欧拉函数,欧拉定理,费马小定理}
欧拉函数 定义 ϕ ( x ) , x ∈ N + \phi(x), \ \ x \in N^+ ϕ(x), x∈N+ means the number of y ∈ N + y \in N^+ y ...
- 费马定理中值定理_数论-欧拉函数、欧拉定理
欧拉函数 积性函数 满足 ( 互质) 定义 对于正整数 ,欧拉函数是小于等于 的所有数中与 互质的数的 个数. 欧拉函数是积性函数(这个证明不是很显然,这个链接里面有很多种证明方法) 记作: 公式 , ...
最新文章
- 用计算机解组合题,计算机组成原理试题解析5
- 如何利用PyTorch中的Moco-V2减少计算约束
- 演讲实录丨王海峰:AI 新基建加速产业智能化
- windows7升级安装之初体验
- 全球大半网络瘫痪,背后原因竟来自这家无名小公司
- Zookeeper服务端线程分析(单机)
- python程序填空程序改错_Python - class dummyclass(object): 改错
- no python interpreter configured
- curl循环监控_阿里巴巴开源性能监控神器Arthas
- H3C FTP配置示例
- 如何在IE让用户自动下载ActiveX控件?
- 在android studio中如何创建一个类来继承另外一个类_在Android使用Transition API检测用户活动...
- 攻城狮算保险--理财型,还是消费型?
- 图灵接口 php,图灵机器人API接口
- 从NVIDIA官方网站上下载CUDA的方法
- 【Anki 牌组+Markdown笔记分享】汇编语言
- 3D大型网络游戏腐竹制作教程
- SciTE 编辑器汉化
- imtoken官网通告SHIB拥有自己的Shibarium区块链,
- centOS7安装 mysql-community-release-el7-5.noarch.rpm 包