poj 1845 Sumdiv(约数和,乘法逆元)

题目:

  AB的正约数之和。

输入:

  A,B(0<=A,B<=5*107

输出:

  一个整数,AB的正约数之和 mod 9901。

 

思路:

  根据正整数唯一分解定理,若一个正整数表示为:A=p1^c* p2^c* ...... pm^cm 则其正约数之和可以表示为:S=(1+p1+p1^2+......p1^c1)*(1+p2+p2^2+......p2^c2)*......(1+pm+pm^2+......pm^cm)

那么AB就可以表示为:S'=(1+p1+p1^2+......p1^(c1*B))*(1+p2+p2^2+......p2^(c2*B))*......(1+pm+pm^2+......pm^(cm*B))

这样,我们发现每一项(以第一项为例)(1+p1+p1^2+......p1^(c1*B))是一个等比数列,根据求和公式易得:(p1^(c1*B+1)-1)/(p1-1)同理,后面的式子也是。那么接下来我们可以通过快速幂求解分子

部分。分母部分需要用到(p1-1)的乘法逆元。因为模数9901是质数,所以只要(p1-1)不是9901的倍数,那么它们就互质,根据费马小定理,乘法逆元就是(p1-2)。特别的,如果(p1-1)9901

的倍数,那么就有(p1-1)|  9901,即:p1%9901=1,所以这一项就变成了:(1+1+1^2+……+1^(c1*B))%9901=(c1*B)+1 (mod 9901) 。具体代码如下:

#include<cstdio>
const int mod=9901;
typedef long long ll;
int a,b,ans=1;
int factor[1000005],fc[1000005],cnt;
void div(int x)
{
    for (int i=2;i*i<=x;i++)
    {
        if (x%i==0) 
        {
            factor[++cnt]=i;
            while (x%i==0) x/=i,fc[cnt]++;
        }
    }
    if (x>1) factor[++cnt]=x,fc[cnt]=1;
}
int ksm(int a,ll b)
{
    int re=1;
    while (b)
    {
        if (b&1) re=(1ll*re*a)%mod;
        a=(1ll*a*a)%mod; b>>=1;
    }
    return re;
}
int main()
{
    scanf ("%d%d",&a,&b);
    div(a);
    for (int i=1;i<=cnt;i++)
    {
        int fac=factor[i];
        if ((fac-1) % 9901 == 0)//特判分母是否是9901的倍数
        {
            ans = (ans%mod * (1ll*b*fc[i]+1)%mod) % mod;
            continue;
        }
        int fm=( ksm(fac,1ll*b*fc[i]+1)-1+mod )%mod;//分母
        int fzny=( ksm(fac-1,mod-2) )%mod;//分子逆元
        ans = (1ll*ans * fm%mod * fzny%mod)%mod;
    }
    printf("%d",ans);
    return 0;
}

 

posted @ 2018-09-02 17:03  zylAK  阅读(325)  评论(0编辑  收藏  举报