因子与因子个数
定义:
求因子个数模板
代码:
#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;
}