Sumdiv(POJ 1845)

Description

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+p11+p12+……+p1c1)*(1+p21+p22+……+p2c2) ……
如果一个个的计算,时间复杂度是比较大的,我们要尽量减少运算次数,就需要推公式
设 c 奇数:2x+1,偶数:2x
 
奇数时:
  1 + p + .... + p+ px+1 + ....+ p2x+1
= ( 1 + p + .... + px ) + px+1*(1+p .... + p)
= sum ( p , x ) * ( 1+px+1 )
 
偶数时:
  1 + p +... + px-1 + p+ px+1 + ... + p2x
= ( 1+ p + px-1 ) + px+1 *( 1+p...+px-1 ) + px
= sum(p,x-1)*(1+px+1) + px
 
Attention:
1、特判边界--- a==1 || b==0
2、sum(p,n) 是指 1~ n ,注意 + 1
 

code【递归/分治做法】

#include<stdio.h> 
#include<algorithm> 
#define ll long long  
using namespace std;
const int mod=9901;
int a,b,ans=1;

ll ksm(ll x,ll k) {
    ll s=1;
    while(k) {
        if(k&1) s=s*x%mod; 
        x=x*x%mod;
        k>>=1;
    }
    return s;
}

int sum(int p,int k) {
    if(k==1) return 1;
    if(k&1) return (sum(p,k/2))*(1+ksm(p,k/2+1))%mod+ksm(p,k/2)%mod; 
    else return sum(p,k/2)*(1+ksm(p,k/2))%mod;
} 

int main() 
{
    scanf("%d%d",&a,&b);
    if(a<=1 || b==0) {
        printf("1");
        return 0;
    }
    int n=a,k=0;
    for(int i=2;i*i<=n;++i) {
        if(a%i==0) {
            k=0;
            while(a%i==0) k++,a/=i;
            ans=(ans*sum(i,k*b+1))%mod;
        }
    }
    if(a>1) ans=(ans*sum(a,b+1))%mod;
    printf("%d",ans);
    return 0;
}

 

还有一种最直接的方法,等比数列求和

 1 + p + p2 + p+ p4 +……+ pb*c

= ( pb*c+1 - 1 ) / ( p - 1 )

因为除法不能直接膜,所以要用逆元,相当于乘它取模与除它等效

根据费马小定理,当p,q互质,pq-2即为他的逆元【费马知道就好】

要特判一下 p 不与 q 互质【也就是它的倍数】,所以直接乘上膜掉就好啦。

 

code【逆元做法】

#include<stdio.h>
#include<algorithm> 
#define ll long long 
const int mod = 9901;
using namespace std;
int a,b;
int p[1100],c[1100],m;

ll ksm(ll x,ll k) {
    ll s=1;
    while(k) {
        if(k&1) s=s*x%mod;
        x=x*x%mod;
        k>>=1;
    }
    return s;
}

void div(int n) 
{
    int d=n;
    for(int i=2;i*i<=n;++i) {
        if(d%i==0) {
            p[++m]=i;
            while(d%i==0) d/=i,c[m]++;
        }
    }
    if(d>1) p[++m]=d,c[m]=1;
}

int main() 
{
    int ans=1;
    scanf("%d%d",&a,&b);
    if(a==1 || b==0) {
        printf("1");
        return 0;
    }
    div(a);
    for(int i=1;i<=m;++i) {
        if((p[i]-1)%mod==0) {
            ans=(long long)(b*c[i]+1)%mod*ans%mod;
            continue;
        }
        int x=ksm(p[i],b*c[i]+1);
        x=(x-1+mod)%mod;
        int y=ksm(p[i]-1,mod-2);
        ans=(long long)ans*x%mod*y%mod;
    }
    printf("%d",ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/qseer/p/9798931.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值