Sumdiv POJ - 1845 (数论)

 

Sumdiv

POJ - 1845

 

 

 

 

Consider two natural numbers A and B. Let S be the sum of all natural divisors of A^B. Determine S modulo 9901 (the rest of the division of S by 9901).

Input

The only line contains the two natural numbers A and B, (0 <= A,B <= 50000000)separated by blanks.

Output

The only line of the output will contain S modulo 9901.

Sample Input

2 3

Sample Output

15

Hint

2^3 = 8.
The natural divisors of 8 are: 1,2,4,8. Their sum is 15.
15 modulo 9901 is 15 (that should be output).

 

这里用到数论的知识只要一下三条性质

 

(1)   整数的唯一分解定理:

      任意正整数都有且只有一种方式写出其素因子的乘积表达式。

      A=(p1^k1)*(p2^k2)*(p3^k3)*....*(pn^kn)   其中pi均为素数

(2)   约数和公式:

对于已经分解的整数A=(p1^k1)*(p2^k2)*(p3^k3)*....*(pn^kn)

有A的所有因子之和为

    S = (1+p1+p1^2+p1^3+...p1^k1) * (1+p2+p2^2+p2^3+….p2^k2) * (1+p3+ p3^3+…+ p3^k3) * .... * (1+pn+pn^2+pn^3+...pn^kn)

(3)   同余模公式:

(a+b)%m=(a%m+b%m)%m

(a*b)%m=(a%m*b%m)%m

 

三条性质环环相扣,首先要求出所有的素因子,然后通过素因子用求和公式求出所有因子和,两种方法的不同就在于用约束和公式时,求等比数列的方式不同

 

方法一、:等比数列公式求和

这种方法很容易想到,但是却有很多的坑

首先第一个坑就是等比数列求和公式的分类讨论

 

①当q≠1时,

②当q=1时,

注意公比取模后为1的情况:

容易忘的就是公比为“”1“”的地方,为什么这里加引号,因为题目要求取模,也就是q有可能实际上并不是1,只是q和1取模同余(即q≡1(mod m)即(q-1)%m=0,这是同余定义)。所以最终取完模后还是按1算

 

其次当q≠1的一般情况下用公式时,因为取模没有对除法的分配律,所以不能分别给分子取模分母取模,又因为我们求分子的时候用的是快速幂取模,所以就无意的把取模分配给了分子,但是又不能直接分配给分母,所以要求出分母的逆元,然后变成乘法,就可以分配了,(乘法可以分配),求逆元的方法有两种费马小定理和扩展欧几里得,这里用费马小定理求较为方便

费马小定理:a^(p-1)≡1(mod p)

那么由此逆元可以求 a^(p-2) ≡ a^(-1) (mod p)

 

code1:

 

#include 
#include 
#include 
#define Mod 9901
using namespace std;
typedef long long ll;
const int MAXN = 1e5+10;
ll q_pow(ll a,ll b){
    ll ans = 1;
    a = a%Mod;
    while(b){
        if(b&1){
            ans = ans*a%Mod;
        }
        a = a*a%Mod;
        b >>= 1;
    }
    return ans%Mod;
}//快速幂

ll getreverse(ll x){
    return q_pow(x,(ll)(Mod-2))%Mod;
}//费马小定理求逆元

ll cal(ll p,ll cnt,ll b){
    ll tmp1 = (q_pow(p,cnt*b+(ll)1)-(ll)1)%Mod;
    ll reversex = getreverse(p-(ll)1)%Mod;
    return tmp1*reversex%Mod;
}//等比数列求和

int main(){
    ll a,b;
    ll p[MAXN];
    ll k[MAXN];
    scanf("%lld%lld",&a,&b);
    ll cnt = 0;
    ll i;
    ll ans = 1;
    for(i = 2; i*i <= a; i++){
        if(a%i==0){
            p[cnt] = i;
            k[cnt] = 0;
            while(a%i==0){
                a /= i;
                k[cnt]++;
            }
            cnt++;
        }
    }
    if(a!=1){
        p[cnt] = a;
        k[cnt++] = 1;
    }
    //注意使用公式法求时也要分公比为“1”和公比不为1的情况
    for(i = 0; i < cnt; i++){
       if((p[i]-1)%Mod==0)//注意注意这里不能写p[i] = 1,因为要取模,所以如果p[i]恰好和1取模Mod同余,即使p[i]不是1最终
                          //算的时候还是按取完模的1计算。
        ans = ans*(1+b*k[i])%Mod;
       else
        ans = ans*cal(p[i],k[i],b)%Mod;//一般情况用公式的时候要注意分母p-1要用费马小定理求出其逆元,因为取模对除法没有分配律
    }
    while(ans<0)
        ans += Mod;
    printf("%lld\n",ans);
    return 0;
}

 

 

 

 

 

方法二、用等比数列递归求和

这种方法就直接解决的分类讨论和取模需要求逆元的困扰,直接递归相乘取模即可

 

(1)若n为奇数,一共有偶数项,则:
      1 + p + p^2 + p^3 +...+ p^n

      = (1+p^(n/2+1)) + p * (1+p^(n/2+1)) +...+ p^(n/2) * (1+p^(n/2+1))
      = (1 + p + p^2 +...+ p^(n/2)) * (1 + p^(n/2+1))

上式红色加粗的前半部分恰好就是原式的一半,那么只需要不断递归二分求和就可以了,后半部分为幂次式,将在下面第4点讲述计算方法。

 

(2)若n为偶数,一共有奇数项,则:
      1 + p + p^2 + p^3 +...+ p^n

      = (1+p^(n/2+1)) + p * (1+p^(n/2+1)) +...+ p^(n/2-1) * (1+p^(n/2+1)) + p^(n/2)
      = (1 + p + p^2 +...+ p^(n/2-1)) * (1+p^(n/2+1)) + p^(n/2);

   上式红色加粗的前半部分恰好就是原式的一半,依然递归求解

code2:

 

#include 
#include 
#include 
#define Mod 9901
using namespace std;
typedef long long ll;
const int MAXN = 1e5+10;
ll q_pow(ll a,ll b){
    ll ans = 1;
    a = a%Mod;
    while(b){
        if(b&1){
            ans = ans*a%Mod;
        }
        a = a*a%Mod;
        b >>= 1;
    }
    return ans%Mod;
}//快速幂

ll sum(ll p, ll n){//递归求等比数列和
    if(n == 0)
        return 1;
    if(n&1)
        return ((1+q_pow(p,n/2+1))%Mod*sum(p,n/2)%Mod)%Mod;
    else
        return ((1+q_pow(p,n/2+1))%Mod*sum(p,(n-1)/2)%Mod+q_pow(p,n/2)%Mod)%Mod;
}

int main(){
    ll a,b;
    ll p[MAXN];
    ll k[MAXN];
    scanf("%lld%lld",&a,&b);
    ll cnt = 0;
    ll i;
    ll ans = 1;
    for(i = 2; i*i <= a; i++){
        if(a%i==0){
            p[cnt] = i;
            k[cnt] = 0;
            while(a%i==0){
                a /= i;
                k[cnt]++;
            }
            cnt++;
        }
    }
    if(a!=1){
        p[cnt] = a;
        k[cnt++] = 1;
    }
    for(i = 0; i < cnt; i++){
        ans = ans*sum(p[i],b*k[i])%Mod;
    }
    printf("%lld\n",ans);
    return 0;
}

 

 

 

 

 

 

 

 

你可能感兴趣的:(#,数论)