前言
引理:对所有素数 p 和 所有整数 a,b,如果 p | ab,则 p | a 或 p | b (或两者都成立)
证明:采用反证法。假设 p | ab,但 p ∤ a 且 p ∤ b。故gcd(a,p)= 1,gcd(b,p)= 1,故gcd(ab,p) = 1,而 gcd(ab,p) = p,矛盾,原命题成立。
定理:如果素数 p ∣ a 1 a 2 a 3 . . . a r p | a _1 a_2a_3...a_r p∣a1a2a3...ar,则 p ∣ a 1 p | a_1 p∣a1 或 p ∣ a 2 p | a_2 p∣a2…或 p ∣ a r p | a_r p∣ar至少一个成立
由上面这个定理我们发现:
①数n可以以某种方式分解成素数乘积
②仅有唯一的因数分解(不考虑因数重排)
一、定理内容
任何一个大于1的整数n 都可以分解成若干个素因数的连乘积,如果不计各个素因数的顺序,那么这种分解是惟一的。
n
=
∏
i
=
1
r
p
i
e
i
n = \prod_{i=1}^r p_i^{e_i}
n=i=1∏rpiei
e
i
e_i
ei为正整数,
p
i
p_i
pi为互不相同的素数。
证明:唯一分解定理
二、素数拆分
给你一个数n,如何将他拆分成素数的乘积呢?
1. 试除法
直接枚举因子然后把当前因子全部除尽即可,时间复杂度 O ( n ) O(\sqrt n) O(n)
int e[N],p[N];
void divide(int n)
{
int cnt = 0;
for(int i=2; i*i<=n; i++)
{
if(n%i == 0)
{
p[++cnt] = i,e[cnt] = 0;
while(n % i == 0)
{
n /= i;
e[cnt]++;
}
}
}
if(n > 1)
p[++cnt] = n,e[cnt]=1;
for(int i = 1;i <= cnt; ++ i)
cout << p[i] << "^" << e[i] << endl;
}
2. Pollard-Rho算法
一种大数(如n>1e18)分解的随机算法
有空再细写
三、应用
1.因子个数
将n分解过后, n = p 1 e 1 ∗ p 2 e 2 ∗ . . . ∗ p r e r n=p_1^{e_1}*p_2^{e_2}*...*p_r^{e_r} n=p1e1∗p2e2∗...∗prer ,n的因子一定是 p i p_i pi的组合,且每个组合的选择有 e i + 1 e_i+1 ei+1个,因此因子个数为 ∏ i = 1 r ( e i + 1 ) \prod_{i=1}^r{(e_i+1)} ∏i=1r(ei+1)
例题:
给定x,y(x,y<231 )问xy的因子数mod10007是多少?
如果强行计算,xy已经是天文数字了,所以我们考虑将他分解为素数的乘积
x
y
=
(
p
1
e
1
∗
p
2
e
2
∗
.
.
.
∗
p
r
e
r
)
y
=
p
1
y
e
1
∗
p
2
y
e
2
∗
.
.
.
∗
p
r
y
e
r
x^y=(p_1^{e_1}*p_2^{e_2}*...*p_r^{e_r})^y=p_1^{ye_1}*p_2^{ye_2}*...*p_r^{ye_r}
xy=(p1e1∗p2e2∗...∗prer)y=p1ye1∗p2ye2∗...∗pryer
答案即为
∏
i
=
1
r
(
y
e
i
+
1
)
m
o
d
10007
\prod_{i=1}^r{(ye_i+1)} mod10007
∏i=1r(yei+1)mod10007
2.因子和
设 S ( n ) S(n) S(n)为因子和,考虑每个 p i , S ( n ) = ( p i 0 + p i 1 + . . . + p i e i ) ∗ ( S ( 剩 余 ) ) p_i,S(n)=(p_i^{0}+p_i^{1}+...+p_i^{e_i}) *(S(剩余)) pi,S(n)=(pi0+pi1+...+piei)∗(S(剩余)),由等比数列求和公式得知因子和为 ∏ i = 1 r p i e i + 1 − 1 p i − 1 \prod_{i=1}^r\frac{p_i^{e_i+1}-1}{p_i-1} ∏i=1rpi−1piei+1−1
例题:
约数之和

思路:与上题类似,要注意的是分数取模的时候,如果p-1 ≡ 0(mod9901),则不存在乘法逆元,p ≡ 1(mod9901),故(1+p+p2 +…+pe*b ) = (1+1+1+…+1) = e*b+1,否则用乘法逆元就可以解决。
#include<iostream>
#include<iomanip>
#include<map>
#include<cstring>
#include<string>
#include<queue>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<ctime>
#include<stack>
#include<set>
#define N 50000005
#define mod 9901
#define IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define PII pair<int,int>
#define mp make_pair
using namespace std;
const int INF = 1000 * 1000 + 5;
typedef long long ll;
//freopen("D:\\in.txt","r",stdin);
//freopen("D:\\in.txt","w",stdout);
using namespace std;
int p[N],c[N];
int tot;
void divide(ll n)
{
for(int i=2; i*i<=n; i++)
{
if(n%i == 0)
{
p[++tot] = i;
c[tot] = 0;
while(n%i==0)
{
n/=i;
c[tot]++;
}
}
}
if(n>1)
{
p[++tot] = n;
c[tot] = 1;
}
}
ll qpow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)
ans = (ans*a)%mod;
a = (a*a)%mod;
b >>= 1;
}
return ans;
}
int main()
{
ll a,b;
scanf("%lld%lld",&a,&b);
if(a==0)
{
printf("0\n");
return 0;
}
divide(a);
ll ans=1;
for(int i=1; i<=tot; i++)
{
if((p[i]-1)%mod == 0)
{
ans = (b*c[i]+1)%mod * ans %mod;
continue;
}
ll x = qpow(p[i],(ll)b*c[i]+1);//分子
x = (x-1+mod) %mod;
ll y = p[i]-1;//分母
y = qpow(y,mod-2);//费马小定理求逆元
ans = ans * x % mod * y % mod;
}
printf("%lld\n",ans);
return 0;
}
3.一些例题
- 阶乘分解

思路:
我们发现N!中的p的个数就是1~N中p的个数和,因此我们只要统计所有 p k i p_k^i pki出现的个数和即可求出答案。
代码:
#include<iostream>
#include<iomanip>
#include<map>
#include<cstring>
#include<string>
#include<queue>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<ctime>
#include<stack>
#include<set>
#define N 1000005
#define mod 998244353
#define IO ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define PII pair<int,int>
#define mp make_pair
using namespace std;
const int INF = 1000 * 1000 + 5;
typedef long long ll;
//freopen("D:\\in.txt","r",stdin);
//freopen("D:\\in.txt","w",stdout);
using namespace std;
int primes[N],cnt;
bool vis[N];
void shai(int n)
{
vis[1]=vis[0]=1;
for(int i=2; i<=n; i++)
{
if(!vis[i]) primes[cnt++] = i;
for(int j=0; primes[j]*i<=n && j<cnt; j++)
{
vis[primes[j]*i] = 1;
if(i%primes[j]==0) break;
}
}
}
int main()
{
IO;
int n;
cin>>n;
shai(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;
cout<<p<<" "<<s<<endl;
}
return 0;
}
变形1:求N!末尾0的个数。
思路:也就是求因子乘积为10的个数,即2,5因子数的最小值,而容易知道5的个数一定比2少,因此计算因子5的个数即可
int ans = 0;
while(n)
{
num += n / 5;
n /= 5;
}
return ans;
类似的要求N!在二进制最低位1出现的位置,也就是求因子2出现的个数
变形2:求N!的因子的因子个数和
思路:
N
!
=
p
1
e
1
∗
p
2
e
2
∗
.
.
.
∗
p
r
e
r
N!=p_1^{e_1}*p_2^{e_2}*...*p_r^{e_r}
N!=p1e1∗p2e2∗...∗prer
p
i
e
i
p_i^{e_i}
piei的选择有
e
i
+
1
种
e_i+1种
ei+1种:
p
i
0
,
p
i
1
,
.
.
.
.
.
.
,
p
i
e
1
p_i^{0},p_i^1,......,p_i^{e_1}
pi0,pi1,......,pie1
p
i
0
p_i^{0}
pi0有1个因子,
p
i
1
p_i^{1}
pi1有2个因子,
p
i
e
1
p_i^{e_1}
pie1有
e
1
+
1
e_1+1
e1+1个因子
故对于
p
i
p_i
pi所贡献的因子数即为
(
e
i
+
1
)
∗
(
e
i
+
2
)
2
\frac{(e_i+1)*(e_i+2)} {2}
2(ei+1)∗(ei+2)
最后再将每一个p的因子数乘起来即是结果了
公式为
∏
i
=
1
r
(
e
i
+
1
)
∗
(
e
i
+
2
)
2
\prod_{i=1}^r\frac{(e_i+1)*(e_i+2)} {2}
∏i=1r2(ei+1)∗(ei+2)
题目链接:Divisors of the Divisors of An Integer(2018-2019 ACM-ICPC, Asia Dhaka Regional ContestC)
参考资料
《算法竞赛中的初等数论》(一)正文 0x00整除、0x10 整除相关(ACM / OI / MO)(十五万字符数论书)
夜深人静写算法(三)- 初等数论入门
这篇博客介绍了素数的唯一分解定理及其在数论中的应用,包括素数拆分方法如试除法和Pollard-Rho算法,以及如何利用这些知识计算因子个数、因子和。通过实例展示了如何解决相关问题,如求解大数因子的因子个数和因子和,并探讨了阶乘分解和末尾0的个数等数学问题。
428

被折叠的 条评论
为什么被折叠?



