数论 —— 约数
【唯一分解定理】
对于任意一个大于 1 的正整数 n,都有且只有一种方式写出其素因子的乘积表达式。
即:,其中,
均为素数
因此,当给出一个数 n 时,可以暴力枚举,以获取 n 的素因子
map<int, int> num;//记录素因子个数
int factor[N],cntF;//记录素因子
void getFactor(int n){
cntF = 0;
num.clear();
for (int i = 2; i <= n / i;i++){
if(n%i == 0)//记录素因子
factor[cntF++] = i;
while(n%i == 0) {
n /= i;
num[i]++;//记录素因子个数
}
}
if(n > 1) {
factor[cntF++] = n;
num[n]++;
}
}
int main(){
int n;
scanf("%d",&n);
getFactor(n);
for(int i=0;i<cntF;i++)
cout<<factor[i]<<":"<<num[factor[i]]<<endl;
return 0;
}
【约数个数函数】
对于一个整数 n,将其通过唯一分解定理分解,有:
那么,n 的因子个数为:
显然,d(n) 是一个积性函数,那么可以用线性筛求出
由于每个数由其最小质因子筛出,因此要开一个辅助数组 num[i] 来表示 i 最小因子的次幂
那么,可分为以下三种情况讨论
1)i 是质数
当 i 为质数时,其约数仅有 1 与其自身,显然存在:d[i]=2,num[i]=1
2)i%prime[j]!=0
当 i%prime[j]!=0 时,i 没有 prime[j] 这个质因子,然而 i*prime[j] 中包含 prime[j]
根据前面得到的 d[i]=(a1+1)(a2+1)...(ak+1)
因此,要加上当前的 prime[j],从而有:d[i*prime[j]]=(a1+1)(a2+1)...(ak+1)(1+1)
即:d[i*prime[j]]=d[i]*d[prime[j]]=d[i]*2
由于当前的 prime[j] 必然是 i*prime[j] 的最小素因子,因此当前最小素因子的个数为:num[i*prime[j]]=1
3)i%prime[j]=0
当 i%prime[j]=0 时,i 中必然包含了至少一个 prime[j],且 prime[j] 必定是 i 的最小质因数
由于 i∗prime[j] 与 i 相比,至少多了一个最小质因子,根据前面得到的 d[i]=(a1+1)(a2+1)...(ak+1)
那么有:d[i∗prime[j]]=(a1+1+1)(a2+1)...(ak+1)
即:d[i∗prime[j]]=d[i]/(a1+1)∗(a1+2)
当前最小素因子的个数也在原来的 num[i] 的基础多了一个,即:num[i∗prime[j]]=num[i]+1
int d[N],num[N];
int prime[N],cnt;
bool bprime[N];
void getFactorNum(int n){
d[1]=1;
num[1]=1;
for(int i=2;i<=n;i++){
if(!bprime[i]){
prime[++cnt]=i;
d[i]=2;
num[i]=1;
}
for(int j=1;j<=cnt&&i*prime[i]<=n;j++){
bprime[i*prime[j]]=true;
if(i%prime[j]!=0){
d[i*prime[j]]=2*d[i];
num[i*prime[j]]=1;
}
else{
d[i*prime[j]]=d[i]/(num[i]+1)*(num[i]+2);
num[i*prime[j]]=num[i]+1;
break;
}
}
}
}
int main(){
int n;
scanf("%d",&n);
getFactorNum(n);
for(int i=1;i<=n;i++)
printf("%d\n",d[i]);
return 0;
}
【约数和函数】
对于一个整数 n,将其通过唯一分解定理分解,有:
其所有因子之和为:
显然,σ(n) 是一个积性函数,那么可以用线性筛求出
设 sp[i] 表示 i 的最小质因子的各次幂之和,即:
那么,可分为以下三种情况讨论:
1)i 是质数时
当 i 为质数时,其约数仅有 1 与其自身,显然存在:sd[i]=i+1,sp[i]=i+1
2)i%prime[j]!=0
当 i%prime[j]!=0 时,i 中不包含 prime[j] 这个素因子,且 prime[j] 一定是 i*prime[j] 的最小素因子
那么由:
得:
即有:sd[i*prime[j])=sd[i]*sd[prime[j]]
相应的,sp[i*prime[j]]=sp[prime[j]]=prime[j]+1
3)i%prime[j]=0
当 i%prime[j]=0 时,i 中至少包含了一个 prime[j]
那么由:
得:
可以发现,两式唯一的不同在于:
那么:sd[i*prime[j]=sd[i] / (1) * (2)
即:sd[i*prime[j]=sd[i]/sp[i]*(sp[i]*prime[j]+1)
相应的,sp[i*prime[j]]=sp[i]*prime[j]+1
int prime[N],cnt;
bool bprime[N];
int sd[N],sp[N];
void getSd(int n){
sd[1]=1;
sp[1]=1;
for(int i=2;i<=n;i++){
if(!bprime[i]){
prime[++cnt]=i;
sd[i]=i+1;
sp[i]=i+1;
}
for(int j=1;j<=cnt&&prime[j]*i<N;j++){
int temp=prime[j]*i;
bprime[temp]=true;
if(i%prime[j]==0){
sp[temp]=sp[i]*prime[j]+1;
sd[temp]=sd[i]/sp[i]*sp[temp];
break;
}
sd[temp]=sd[i]*sd[prime[j]];
sp[temp]=prime[j]+1;
}
}
}
int main(){
int n;
scanf("%d",&n);
getSd(n);
for(int i=1;i<=n;i++)
printf("%d\n",d[i]);
}
【最高次幂】
对于一个整数 n,若要求满足 n=a^p 这个式子最大的 p,其中 n 可能是负数,a、p 是整数
则可知:p = GCD(k1,k2,k3,...,kn)
比如:24 = 2^3*3^1,p 应该是 GCD(3, 1) = 1,即:24 = 24^1
324 = 3^4*2^2,p应该是 GCD(4, 2) = 2,即:324 = 18^2
int prime[N],cnt;
bool bprime[N];
void make_prime(){
memset(bprime,false,sizeof(bprime));
for(int i=2;i<=N;i++){
if(!bprime[i]){
prime[cnt++]=i;
for(LL j=i*2;j<=N;j+=i)
bprime[j]=true;
}
}
}
int GCD(int a, int b){
return a % b == 0 ? b : GCD(b, a % b);
}
int main(){
makePrime();
int n;
scanf("%d",&n);
bool flag=false;
if(n<0){//负数的情况
n=-;
flag=true;
}
int res=0;
for(int i=0;i<cnt&&prime[i]<=n;i++){
if(n%prime[i]==0){//枚举每一组的prime^k
int k=0;
while(n%prime[i]==0){
k++;
n/=prime[i];
}
//求p=GCD(k1,k2,...,kn)
if(res==0)
res=k;
else
res=GCD(res,k);
}
}
if(n>1)//若质数除不尽
res=GCD(res,1);
if(flag){//负数时,幂不可能为偶数
while(res%2==0)
res/=2;
}
printf("%d\n",res);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下