【POJ 1845】sumdiv约数和

FZOJ1831: 【POJ 1845】sumdiv约数和

FZOJ传送门

首先,我们得知道A的约数等于什么
A = p 1 k 1 ∗ p 2 k 2 ∗ ⋯ ∗ p n k n A=p_{1}^{k_{1}}*p_{2}^{k_{2}}* \cdots*p_{n}^{k_{n}} A=p1k1p2k2pnkn其中pn为质数,kn为任意整数。
根据幂的运算,易得
A B = p 1 k 1 B ∗ p 2 k 2 B ∗ ⋯ ∗ p n k n B A^{B}=p_{1}^{k_{1}B}*p_{2}^{k_{2}B}* \cdots*p_{n}^{k_{n}B} AB=p1k1Bp2k2BpnknB其次,我们还有一个约数和公式
S = ( 1 + p 1 + p 1 2 + ⋯ + p 1 k 1 ) ∗ ( 1 + p 2 + p 2 2 + ⋯ + p 2 k 1 ) ⋯ S=(1+p_{1}+p_{1}^{2}+\cdots+p_{1}^{k_{1}})*(1+p_{2}+p_{2}^{2}+\cdots+p_{2}^{k_{1}})\cdots S=(1+p1+p12++p1k1)(1+p2+p22++p2k1)根据等比数列以及上面的公式知:
S = ( 1 − p 1 k 1 B + 1 1 − p 1 ) ∗ ( 1 − p 2 k 2 B + 1 1 − p 2 ) ⋯ S=(\frac{1-p_1^{k_1B+1}}{1-p_1})*(\frac{1-p_2^{k_2B+1}}{1-p_2})\cdots S=(1p11p1k1B+1)(1p21p2k2B+1)
即:
S = ( p 1 k 1 B + 1 − 1 p 1 − 1 ) ∗ ( p 2 k 2 B + 1 − 1 p 2 − 1 ) ⋯ S=(\frac{p_1^{k_1B+1}-1}{p_1-1})*(\frac{p_2^{k_2B+1}-1}{p_2-1})\cdots S=(p11p1k1B+11)(p21p2k2B+11)

直接暴力求解等比数列可能会超时,所以很自然地想到了逆元(费马小定理),但是此时有一点要注意:pi-1可能是9901的倍数,就会不存在逆元。
对于 A B ≡ x ( m o d   m ) \frac{A}{B} \equiv x(mod\ m) BAx(mod m)
等价于:
A B + k m = x \frac{A}{B}+km= x BA+km=x通分得:
A + k B m B = x \frac{A+kBm}{B}= x BA+kBm=x即:
A ( m o d   B m ) B = x \frac{A(mod\ Bm)}{B} = x BA(mod Bm)=x
应用这个公式,用到快速幂取模,在快速幂取模中一步乘法可能会溢出long long ,因此有添加了一个对于大数a*b (mod m)的函数。因为最终的模数不是mod,而是 m o d ∗ ( k i − 1 ) mod*(k_{i}-1) mod(ki1)

贴代码

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;

const int mac=50009;
const int MOD=9901;
ll B,n,num;

struct node{
    ll p,k;
}yin[mac];

inline ll mul(ll a,ll b,ll mod){//防止爆long long
    ll s=0;
    while(b){
        if(b&1)
            s=(s+a)%mod;
        a=(a*2)%mod;
        b>>=1;
    }
    return s<0?s+mod:s;
}

inline ll mi(ll a,ll b,ll mod){
    ll sum=1;
    while(b>0){
        if(b&1) sum=mul(sum,a,mod);
        a=mul(a,a,mod);
        b>>=1;
    }
    return sum<0?(sum+mod):sum;
}

int main(){
    // freopen("1831.in","r",stdin);
    scanf("%lld%lld",&n,&B);
    ll A=n;
    num=1;
    for(ll i=2;i<=sqrt(n+0.0);i++){
        if(n%i==0){
            yin[num++].p=i;
            while(n%i==0){
                yin[num-1].k++;
                n/=i;
            }
        }
    }
    if(n>1){//如果a 为素数
            yin[num++].p=n;
            yin[num-1].k++;
        }

    ll ans=1;
    for(ll i=1;i<=num;i++){
        ll b=((yin[i].k)*(B));
        ll p=mi(yin[i].p , b+1, MOD*(yin[i].p-1));
        ans*=(p-1)/(yin[i].p-1);
        ans%=MOD;
    }
    printf("%lld\n",ans);
    return 0;
}

感谢博主Sharp_Edge_Of_Defens的博客,在他的启发下,本人才做出这道题。
参考自 https://blog.csdn.net/u012860428/article/details/41315081

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值