poj 1845 Sumdiv (等比求和+逆元)

题目链接:http://poj.org/problem?id=1845

题目大意:给出两个自然数a,b,求a^b的所有自然数因子的和模上9901 (0 <= a,b <= 50000000)

解题思路:我们先利用唯一分解定理,将a分解成(p1^q1)*(p2^q2)……(pk^qk)的形式,则a^b=((p1^q1)*(p2^q2)……(pk^qk))^b=(p1^q1b)*(p2^q2b)……(pk^qkb)

a^b的因子和就会等于(1+p1+p1^2+……p1^q1b)*(1+p2+p2^2+……p2^q2b)*……(1+pk+pk^2+……pk^qkb)

然后我们可以用等差求和公式转化为((p1^(q1b+1)-1)/(p1-1))*((p2^(q2b+1)-1)/(p2-1))……((pk^(qkb+1)-1)/(pk-1))

对于求逆元:

(a/b)%mod=(a%(mod*b))/b%mod。对B*mod取余,剩余的值必定是B的倍数,这种方法是用于mod和B小的时候,用在这题就刚好了。

#include<iostream>
using namespace std;
typedef long long ll;
const int MAXN=500005;
const int mod=9901;
ll a,b,prime[MAXN],tot;
void getPrime(int N){  //筛素数 
    for(int i=1;i<=N;i++) prime[i]=1;
    for(int i=2;i<=N;i++){
        if(prime[i])
            prime[tot++]=i;
        for(int j=0;j<tot&&prime[j]*i<=N;j++){
            prime[i*prime[j]]=0;
            if(i%prime[j]==0)break;
        }
    }
}
ll qmul(ll a,ll b,ll m){
    ll res=0;
    while(b){
        if(b&1) res=(res+a)%m;
        b>>=1;
        a=(a+a)%m;
    }
    return res;
}
ll qpow(ll a,ll b,ll m){
    ll res=1;
    while(b){
        if(b&1) res=qmul(res,a,m);  //直接相乘会爆,可以一个一个加 
        a=qmul(a,a,m);
        b>>=1;
    }
    return res;
}
ll solve(ll x,ll y){
    ll ans=1;
    for(int i=0;prime[i]*prime[i]<=x;i++){
        if(x%prime[i]==0){
            int cnt=0;
            while(x%prime[i]==0){
                cnt++;
                x/=prime[i];
            }
            ll M=(prime[i]-1)*mod;
            ans=ans*(qpow(prime[i],cnt*y+1,M)-1+M)%M/(prime[i]-1)%mod;
        }
    }
    if(x>1){
        ll M=(x-1)*mod;
        ans=ans*(qpow(x,y+1,M)-1+M)%M/(x-1)%mod;
    }
    return ans;
}
int main(){
    cin>>a>>b;
    getPrime(500000);
    cout<<solve(a,b)<<endl;
    return 0;
}

 

posted @ 2019-05-05 20:38  两点够吗  阅读(163)  评论(0编辑  收藏  举报