因子和与因子个数

摘抄于《ACM-ICPC 程序设计系列数论及应用》

基本理论

  定义1:因子和函数σ定义为整数n的所有正因子之和,记为σ(n).

       定义2:因子个数函数τ定义为正整数n的所有正因子个数,记为τ(n).

定理 

  定理1:如果f是积性函数F(n),那么f的和函数F(n)=Σ(d|n)  f(d) 也是积性函数

推论:因子和函数σ与因子个数函数τ是积性函数(只要令 f(n)=n  和f(n)=1即可)

  定理2:设p是一个素数,a是一个正整数,那么

      σ(pa)=1+p+p2+p3+..+pa= (pa+1-1)/(p-1)  (等比数列求和公式可得)

                    τ(pa)=a+1;      (pa的因子为 1 ,p,p2,p3,...pa)

  定理3:设正整数n有素因子分解n=p1a1*p2a2*.....psas (唯一分解定理)

  σ(n)= (p1a1+1-1)/(p1-1) * (p2a2+1-1)/(p2-1) *(psas+1-1)/(ps-1)  = Π( j=1,s)  (pjaj+1-1)/(pj-1) (由定理2得)

  τ(n)=(a1+1)*(a2+1)*...*(as+1)= Π( j=1,s) (aj+1)

代码实现:

  因为求因子和函数与因子个数函数同求欧拉函数值一样,都需要素因子分解。代码写法写法大致一样。

求因子和:

int sum(int n)
{
    int s=1;
    for(int i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            int a=1;
            while(n%i==0)
            {
                n/=i;
                a*=i;
            }
            s=s*(a*i-1)/(i-1);
        }
    }
    if(n>1)
        s=s*(1+n);
    return s;
}

求因子个数:

ll count(ll n)
{
    ll s=1;
    for(ll i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            ll num=0;
            while(n%i==0)
            {
                n/=i;
                num++;
            }
            s=s*(num+1);
        }
    }
    if(n>1)
        s=s*2;
    return s;
}

 

实战:

  poj 2992(因子个数):求出C(k,n)因子个数  (0<=k<=n<=431)

  思路:

  如果直接求组合数是不可能的因为,其太大了。

  要求因数个数,以上可知,要先求素因子,所以要素因子分解,组合数可以写成阶乘的形式,那可以阶乘素因子分解,求阶乘对于每个素因子可分解的个数。

  步骤:

  1.先筛出431内的素数。

      2.对每个阶乘进行素因子分解

      3.根据上面的定理求因子个数

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
bool isprime[450];
int nprime;
int prime[100];

void init()//筛出431内所有的素数 
{
    nprime=0;
    memset(isprime,1,sizeof(isprime));
    isprime[0]=isprime[1]=0;
    for(int i=2;i<450;i++)
    {
        if(isprime[i])
        {
            nprime++;
            prime[nprime]=i;
            for(int j=2*i;j<450;j+=i)
                isprime[j]=0;
        }
            
    }
}

int main()
{
    init();
    int jie[450][100];//jie[i][j]表示i阶乘分解成第j个素数有几个
    memset(jie,0,sizeof(jie));
    for(int i=1;i<=nprime;i++)
    {
        for(int j=2;j<450;j++)//求出每个阶乘对于每个素数的个数 
            jie[j][i]=j/prime[i]+jie[j/prime[i]][i];
    }
    
    ll c[450][450];
    for(int i=2;i<450;i++) 
    {
        for(int j=1;j<i;j++)
        {
            c[i][j]=1;
            for(int k=1;k<=nprime&&jie[i][k];k++)
            {
                int s=jie[i][k]-jie[j][k]-jie[i-j][k];//C(i,j)= i!/(j!*(i-j)!) 
                if(s)
                    c[i][j]*=(s+1);//求因子个数 
            }
        }
    } 
    
    int n,k;
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        if(!k||k==n)    
            printf("1\n");
        else
            printf("%lld\n",c[n][k]);
    }
    return 0;
} 

 

  poj 1845(求因子和):求 AB%9901,(0<=A,B<=50000000)

思路:

对于A素因子分解为A=p1k1*p2k2*...*pmkm,那么AB=p1k1B*p2k2B*...*pmkmB

因为积性函数的性质,S=(1+p1+p12+...+p1k1B) * (1+p2+p22+..+p2k2B)*...*(1+pm+pm2+..+pmkmB)

由等比数列求和得:S=(p1k1B+1-1)/(p1-1) * (p2k2B+1-1)/(p2-1) *...* (pmkmB+1-1)/(pm-1)

 

依然要素因子分解,这里用二维数组su[][]用于存素因子和其个数,su[i][0]表示第i个素因子为多少,su[i][1]表示其个数。

再根据推导公式求即可,但是除数(p-1)是不能取模的,要变成乘以逆元,因为9901是素数,直接用费马小定理即可

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;

ll mul(ll x,ll y)//快速幂 
{
    ll ans=1;
    while(y)
    {
        if(y&1)
            ans=(ans*x)%9901;
        x=(x*x)%9901;
        y>>=1;
    }
    return ans;
}

int main()
{
    ll a,b;
    ll su[101][2];
    while(scanf("%lld%lld",&a,&b)!=EOF)
    {
        if(a==0)
            printf("0\n");
        else if(a==1||b==0)
            printf("1\n");
        else
        {
            ll p=0;
            for(int i=2;i*i<=a;i++)//素因子分解,求个数 
            {
                if(a%i==0)
                {
                    su[p][0]=i;
                    su[p][1]=0;
                    while(a%i==0)
                    {
                        a/=i;
                        su[p][1]++;
                    }
                    p++;
                }
            }
            if(a>1)
            {
                su[p][0]=a;
                su[p][1]=1;
                p++;
            }
            for(int i=0;i<p;i++)//记得素因子个数是 k*B+1 
            {
                su[i][1]*=b;
                su[i][1]++; 
            }
            ll m=1;
            ll x,y;
            for(int i=0;i<p;i++)
            {
                if(su[i][0]%9901==0)    continue;
                if(su[i][0]%9901==1)
                {
                    m=m*su[i][1]%9901;
                    continue;
                }
                m=m*(mul(su[i][0],su[i][1])-1)%9901;//p^(k*B+1)-1 
                x=mul(su[i][0]-1,9899)%9901;//费马小定理,求(p-1)的逆元 
                x=(x%9901+9901)%9901;//一项素因子的值 
                m=m*x%9901;
            }
            printf("%lld\n",m);
        }
    }
    return 0;
}

 

 

posted @ 2019-05-16 21:43  怀揣少年梦.#  阅读(2377)  评论(0编辑  收藏  举报