约数
唯一分解定理:任何一个大于1的数都可以被分解成有限个质数乘积的形式 $N=\prod_{i=1}{m}p_{i}{c_i} $
N
N
N的正约数个数为:
(
c
1
+
1
)
×
(
c
2
+
1
)
×
.
.
.
×
(
c
m
+
1
)
=
∏
i
=
1
m
(
c
i
+
1
)
(c_1+1)\times (c_2+1)\times ...\times (c_m+1)=\prod_{i=1}^{m}(c_i+1)
(c1+1)×(c2+1)×...×(cm+1)=∏i=1m(ci+1)
N
M
N^M
NM的正约数个数为:
(
M
×
c
1
+
1
)
×
(
M
×
c
2
+
1
)
×
.
.
.
×
(
M
×
c
m
+
1
)
=
∏
i
=
1
m
(
M
×
c
i
+
1
)
(M\times c_1+1)\times (M\times c_2+1)\times ...\times (M\times c_m+1)=\prod_{i=1}^{m}(M\times c_i+1)
(M×c1+1)×(M×c2+1)×...×(M×cm+1)=∏i=1m(M×ci+1)
N
N
N的所有正约数之和为:
σ
(
n
)
=
(
1
+
p
1
+
p
1
2
+
.
.
.
+
p
1
c
1
)
×
.
.
.
×
(
1
+
p
m
+
p
m
2
+
.
.
.
+
p
m
c
m
)
\sigma (n)=(1+p_1+p_{1}^{2}+...+p_{1}^{c_1})\times ...\times (1+p_m+p_{m}^{2}+...+p_{m}^{c_m})
σ(n)=(1+p1+p12+...+p1c1)×...×(1+pm+pm2+...+pmcm) (每一块乘积是一个等比数列)
随机数据下,约数个数的期望是 O ( ln n ) O(\ln_{}{n}) O(lnn)。一个数的约数个数上界是 2 N 2\sqrt N 2N。
例题:
题意:求
1
∼
n
1\sim n
1∼n中,
σ
(
n
)
\sigma (n)
σ(n)是偶数的数的个数。
思路:(主要要自己去推导分析)根据公式:
σ
(
n
)
=
(
1
+
p
1
+
p
1
2
+
.
.
.
+
p
1
c
1
)
×
.
.
.
×
(
1
+
p
m
+
p
m
2
+
.
.
.
+
p
m
c
m
)
\sigma (n)=(1+p_1+p_{1}^{2}+...+p_{1}^{c_1})\times ...\times (1+p_m+p_{m}^{2}+...+p_{m}^{c_m})
σ(n)=(1+p1+p12+...+p1c1)×...×(1+pm+pm2+...+pmcm),当乘积的每一项都为奇数时,
σ
(
n
)
\sigma(n)
σ(n)是奇数。当
p
=
2
p=2
p=2时,
c
c
c为任意值;当
p
!
=
2
p!=2
p!=2时,
c
c
c为偶数 时,
σ
(
n
)
\sigma(n)
σ(n)为奇数。当2及其它质数的指数都取偶数时(即平方数),有
n
\sqrt n
n个数;当2的指数取奇数,其他质数的指数取偶数时,有
n
/
2
\sqrt{n/2}
n/2个。
#include <bits/stdc++.h>
using namespace std;
#define int long long
int T,n,cs;
signed main(){
scanf("%lld",&T);
while(T--){
scanf("%lld",&n);
int ans=n;
ans-=(int)sqrt(n);
ans-=(int)sqrt(n/2);
printf("Case %lld: %lld\n",++cs,ans);
}
}
求一个数的正约数
复杂度: O ( N ) O(\sqrt N) O(N).
for(int i=1;i*i<=n;++i){
if(n%i==0){
printf("%d ",i);
if(n/i!=i) printf("%d ",n/i);
}
}
如果能知道一个大数(1e18)的质因子分解情况,可以直接dfs枚举幂次,搞出这个大数的所有因子,效率更快!
例题:E2. Divisible Numbers (hard version)
多次求解一个很大的数的正约数个数
比一个一个 O ( N ) O(\sqrt N) O(N)去求解要更快。
//先欧拉筛筛出 1~根号n 的质因子
void prime(int n){
for(int i=2;i<=n;++i){
if(!vis[i]){
vis[i]=1;
pri[++cnt]=i;
}
for(int j=1;pri[j]<=n/i;++j){
vis[pri[j]*i]=1;
if(i%pri[j]==0) break;
}
}
}
//然后利用定理结论 计算正约数的个数
//根据筛出来的质数对数n进行质因数分解
int cal(int n){
int ans=1;
for(int i=1;i<=cnt;++i){
if(pri[i]>n) break;
if(n%pri[i]==0){
int num=0;
while(n%pri[i]==0) num++,n/=pri[i];
ans*=(num+1);
}
}
if(n>1) ans*=2;
return ans;
}
例题:
LightOJ-1341 Aladdin and the Flying Carpet
题意:多组输入,每次给定
a
,
b
a,b
a,b,求解
a
a
a的因子数,要求因子大于
b
b
b,且小于等于
(
a
)
\sqrt(a)
(a),若
(
a
)
×
(
a
)
=
=
a
\sqrt(a)\times \sqrt(a)==a
(a)×(a)==a,
(
a
)
\sqrt(a)
(a)不取。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+10;
int cnt=0,pri[N];
bool vis[N];
void prime(int n){
for(int i=2;i<=n;++i){
if(!vis[i]){
vis[i]=1;
pri[++cnt]=i;
}
for(int j=1;pri[j]<=n/i;++j){
vis[pri[j]*i]=1;
if(i%pri[j]==0) break;
}
}
}
int cal(int n){
int ans=1;
for(int i=1;i<=cnt;++i){
if(pri[i]>n) break;
if(n%pri[i]==0){
int num=0;
while(n%pri[i]==0) num++,n/=pri[i];
ans*=(num+1);
}
}
if(n>1) ans*=2;
return ans/2;
}
int T,a,b,cs;
signed main(){
prime(1000000);
scanf("%lld",&T);
while(T--){
scanf("%lld%lld",&a,&b);
if(b*b>=a) printf("Case %lld: %lld\n",++cs,0ll);
else{
int ans=cal(a);
for(int i=1;i<b;++i) if(a%i==0) ans--;
printf("Case %lld: %lld\n",++cs,ans);
}
}
}
求 1 ∼ n 1\sim n 1∼n中每个数的正约数集合
时间复杂度:
O
(
n
log
n
)
O(n\log_{}{n})
O(nlogn)
倍数法的思路是,对于每个数d,它的倍数
2
d
,
3
d
,
4
d
…
…
2d,3d,4d……
2d,3d,4d……等等的约数里就有
d
d
d。所以时间复杂度为
O
(
n
+
n
2
+
n
3
+
.
.
+
n
n
)
≈
O
(
n
log
n
)
O(n+\frac{n}{2} +\frac{n}{3} +..+\frac{n}{n})\approx O(n\log_{}{n})
O(n+2n+3n+..+nn)≈O(nlogn)。
常用与一些于因子有关的计算,如计算 $\sum_{i=1}^{n} \sum_{d|n} d $ 或者
∑
i
=
1
n
∑
d
∣
n
f
(
d
)
\sum_{i=1}^{n} \sum_{d|n} f(d)
∑i=1n∑d∣nf(d).
#include <bits/stdc++.h>
using namespace std;
int n;
vector<int> factor[100000];
int main(){
cin>>n;
for(int i=1;i<=n;++i) {
for(int j=1;j*i<=n;++j) { //1倍的i,2倍的i,3倍的i,...,都有i
factor[i*j].push_back(i); //因子从小到大被求出 //写成 factor[i*j].push_back(j)的话,因子从大到小被求出
}
}
/*另一种写法
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j+=i){ //1倍的i,2倍的i,3倍的i,...,都有i
factor[j].push_back(i);
}
}
*/
for(int i=1;i<=n;++i) {
cout<<i<<": ";
for(int j=0;j<factor[i].size();++j) cout<<factor[i][j]<<' ';
cout<<endl;
}
}
求 1 ∼ n 1\sim n 1∼n中每个数的质因子集合
欧拉筛中求得
vector<int> v[N];
int cnt=0,pri[N];
bool vis[N];
void prime(int n){
for(int i=2;i<=n;++i){
if(!vis[i]){
pri[++cnt]=i;
v[i].pb(i);
vis[i]=true;
}
for(int j=1;pri[j]<=n/i;++j){
vis[pri[j]*i]=true;
v[pri[j]*i]=v[i];
if(i%pri[j]==0) break;
v[pri[j]*i].pb(pri[j]);
}
}
}
相关例题:
Acwing-97.约数之和
求解
A
B
A^B
AB的所有约数之和,
0
<
=
A
,
B
<
=
5
e
7
0<=A,B<=5e7
0<=A,B<=5e7.
做法:质因数分解
A
A
A,然后运用 约数之和公式+等比数列公式 求解。
有一个细节说明:
在求乘法逆元时,当p[i]-1
是MOD的倍数时,即
p
[
i
]
p[i]
p[i]不存在MOD的逆元,即
p
[
i
]
−
1
≡
0
(
m
o
d
M
O
D
)
p[i]-1\equiv 0\pmod{MOD}
p[i]−1≡0(modMOD),即
p
[
i
]
≡
1
(
m
o
d
M
O
D
)
p[i]\equiv 1\pmod{MOD}
p[i]≡1(modMOD),所以
1
+
p
i
+
p
i
2
+
.
.
.
+
p
i
c
i
≡
1
+
1
+
1
+
.
.
.
+
1
≡
c
i
∗
b
+
1
(
m
o
d
M
O
D
)
1+p_i+p_{i}^{2}+...+p_{i}^{c_i}\equiv 1+1+1+...+1\equiv c_i*b+1 \pmod{MOD}
1+pi+pi2+...+pici≡1+1+1+...+1≡ci∗b+1(modMOD).
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e2;
const int MOD=9901;
int a,b;
int p[N],c[N],cnt=0;
void divide(int x){
for(int i=2;i<=sqrt(x);++i){
if(x%i==0) p[++cnt]=i;
while(x%i==0){
x/=i;
c[cnt]++;
}
}
if(x>1) p[++cnt]=x,c[cnt]=1;
}
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1) ans=ans*a%MOD;
a=a*a%MOD;
b>>=1;
}
return ans;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>a>>b;
int ans=1;
divide(a);
for(int i=1;i<=cnt;++i){
if((p[i]-1)%MOD==0){
ans=(c[i]*b+1)*ans%MOD;
}
else{
ans=(qpow(p[i],c[i]*b+1)-1+MOD)%MOD*qpow(p[i]-1,MOD-2)%MOD*ans%MOD; //指数不能取mod
}
}
if(a) cout<<ans<<'\n'; //卡0点
else cout<<0<<'\n';
}