因子与因子个数,以及因子和(超详细推导+讲解) 每日一遍,算法再见!

因子与因子个数

定义:在这里插入图片描述

求因子个数模板

代码:

#include<bits/stdc++.h>
using namespace std;
int get_count(int x)
{
	int ans=1;
	for(int i=2;i*i<=x;i++)
	{
		int a=0;
		if(x%i==0)
		{
			while(x%i==0)
			{
				a++;
				x/=i;
			}
		}
		ans=ans*(a+1);
	}
	/*这里要检查x是否为1,因为上面的循环时优化的可能会导致x的质因子没除完,比如7*/ 
	if(x>1) ans=ans*2; 
	return ans;
}
int main(){
	int x;
	cin>>x;
	int ans=get_count(x);
	cout<<ans;
	return 0;
}

所有因子和的求法

我们在求因子个数的时候顺便就求取因子的和(套公式即可),不过需要提醒的一点是如果数很大的话需要取模,我们的除法不可以直接除,而是乘以它的模意义下的逆元

这里我们展示一个比较小的数的因子和的求法
代码:

#include<bits/stdc++.h>
using namespace std;
int ksm(int dishu,int zhishu){
	int ans=1;
	while(zhishu)
	{
		if(zhishu&1)
		{
			zhishu--;
			ans=ans*dishu;
		}
		else
		{
			zhishu>>=1;
			dishu=dishu*dishu;
		}
	}
	return ans;
}
int get_sum(int x)
{
	int ans=1;
	for(int i=2;i*i<=x;i++)
	{
		int a=0;
		if(x%i==0)
		{
			while(x%i==0)
			{
				a++;
				x/=i;
			}
		}
		int getzhi = (ksm(i,a+1)-1)/(i-1); 
		ans*=getzhi;
	}
	/*防止有素因子没除完*/
	if(x>1) 
	{
		int getzhi = (ksm(x,2)-1)/(x-1); 
		ans*=getzhi;
	} 
	return ans;
}
int main(){
	int x;
	cin>>x;
	int ans=get_sum(x);
	cout<<ans;
	return 0;
}

下面我们再来一个取模意义下的因子求和(如果因子和很大,分子可能超long long题目会要求取模,就必须用到),这里的模给你的一般是一个素数,通常是1e9+7。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
/*求逆元,我们这里用费马小定理来求,也可以用扩展欧几里得,我选择简单快捷的*/
ll get_inv(ll dishu,ll zhishu)
{
	/*其实完全就是ksm的求取方法*/
	ll ans=1;
   while(zhishu)
	{
		if(zhishu&1){
			zhishu--;
			ans=ans*dishu%mod;
		}
		else
		{
			zhishu>>=1;
			dishu=dishu%mod*dishu%mod;
		}
	}
	return ans;
} 
/*快速幂*/
ll ksm(ll dishu,ll zhishu)
{
	ll ans=1;
	while(zhishu)
	{
		if(zhishu&1){
			zhishu--;
			ans=ans*dishu%mod;
		}
		else
		{
			zhishu>>=1;
			dishu=dishu%mod*dishu%mod;
		}
	}
	return ans;
}
ll get_sum(ll x)
{
	ll ans=1; 
	for(ll i=2;i*i<=x;i++)
	{
		ll a=0;
		if(x%i==0)
		{
			while(x%i==0)
			{
				a++;
				x/=i;
			}
			ll inv=get_inv(i-1,mod-2);
			ll zhi=(ksm(i,a+1)-1);
			if(zhi<0) zhi+=mod;//防止出现负数
			zhi=zhi*inv%mod;
			ans=ans%mod*zhi%mod;
		}
	}
	if(x>1)
	{
		    ll inv=get_inv(x-1,mod-2);
			ll zhi=(ksm(x,2)-1);
			if(zhi<0) zhi+=mod;//防止出现负数
			zhi=zhi*inv%mod;
			ans=ans%mod*zhi%mod;
	}
	return ans;
}
int main()
{
     ll x;
	 while(cin>>x)
	 {

	 ll ans=get_sum(x);
	 cout<<ans<<'\n';
      }
	 return 0;	
}

来一道题,练习一下Divisors求约数个数

题目分析:这是一个组合数,我们可以把这个组合数表示为分子乘积/分母乘积的形式,然后把分子分母质因数分解+约分得到一个素数乘积的形式,这样就可以按照求约数个数的公式来做了,题目没有要求取模,说明结果不会太大,但是我们不能直接通过暴力求阶乘的每一个素因子来做,需要用到快速求n!的素因子p的个数,还有注意开long long。

快速求取n!中素因子p的个数
原理:在这里插入图片描述代码:

int solve(int n,int p)
{
	if(n<p) return 0;
	else return (n/p+solve(n/p,p));
}

优化:其实对于某些已求的solve(n,p),我们可以用一个二维数组记录下来,下一次再用到的时候就不用再求一次了,可以节约很多时间,也叫记忆化搜索

优化代码:

int solve(int n,int p)//求n!中因子p的个数 
{
	if(dp[n][p]) return dp[n][p];
	if(n<p) return 0;
	else return dp[n][p]=(n/p+solve(n/p,p));
}

题目AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
ll prime[500]={0};
bool vis[500];
ll dp[500][500]={0};
ll ans[500][500]={0};
void get_prime()
{
	memset(vis,false,sizeof(vis));
	vis[1]=true;
	for(int i=2;i<=431;i++)
	{
		if(!vis[i])
		{
			prime[++prime[0]]=i;
			vis[i]=true;
		}
		for(int j=1;j<=prime[0]&&i*prime[j]<=431;j++)
		{
			vis[prime[j]*i]=true;
			if(i%prime[j]==0) break;
		}
	}
	return ;
}
int solve(int n,int p)//求n!中因子p的个数 
{
	if(dp[n][p]) return dp[n][p];
	if(n<p) return 0;
	else return dp[n][p]=(n/p+solve(n/p,p));
}
int main()
{
	get_prime();
	ll n,k;
	while(cin>>n>>k)
	{
		if(ans[n][k]) printf("%lld\n",ans[n][k]);
		else
		{
		ll res=1;
		for(int i=1;i<=prime[0];i++)
		{
			res=res*(solve(n,prime[i])-solve(n-k,prime[i])-solve(k,prime[i])+1);
		}
		ans[n][k]=res;
		printf("%lld\n",res);
	    }
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值