• 因子和与因子个数


    摘抄于《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
    ");
            else
                printf("%lld
    ",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
    ");
            else if(a==1||b==0)
                printf("1
    ");
            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
    ",m);
            }
        }
        return 0;
    }

     

  • 相关阅读:
    android模拟器速度问题
    input.nextLine() 问题出错!
    android中的“visible ”、“invisible”、“gone”的区别(转载)
    为什么匿名内部类参数必须为final类型(转载)
    转载------------------关于android的一些技巧
    关于数据库的数据类型
    关于几个新的快捷键
    目标

    巨大bug
  • 原文地址:https://www.cnblogs.com/xiongtao/p/10878249.html
Copyright © 2020-2023  润新知