AcWing97------约数之和

题面

首先我们知道一个数可以分解成以下形式

X = p 1 a 1 ∗ . . . ∗ p n a n X=p_1^{a_1}*...*p_n^{a_n} X=p1a1...pnan

则约数和为 ( 1 + p 1 1 + p 1 2 + . . . + p 1 a 1 ) ∗ . . . ∗ ( 1 + p n 1 + p n 2 + . . . + p n a n ) (1+p_1^1+p_1^2+...+p_1^{a_1})*...*(1+p_n^1+p_n^2+...+p_n^{a_n}) (1+p11+p12+...+p1a1)...(1+pn1+pn2+...+pnan)

对于 x y = ( p 1 a 1 ∗ . . . ∗ p n a n ) y = ( p 1 a 1 ∗ y ∗ . . . ∗ p n a n ∗ y ) x^y = (p_1^{a_1}*...*p_n^{a_n})^y = (p_1^{a_1*y}*...*p_n^{a_n*y}) xy=(p1a1...pnan)y=(p1a1y...pnany)

同理可以写出约数和,这里省略;

思路一、分治

我们设 s u m ( p , k ) = p 0 + p 1 + . . . + p k − 1 sum(p,k) = p^0+p^1+...+p^{k-1} sum(p,k)=p0+p1+...+pk1

k k k为偶数时,上式可以改写成 ( p 0 + p 1 + . . . + p k / 2 − 1 ) + ( p k / 2 + . . . + p k − 1 ) (p^0+p^1+...+p^{k/2-1})+(p^{k/2}+...+p^{k-1}) (p0+p1+...+pk/21)+(pk/2+...+pk1)
==> s u m ( p , k / 2 ) + p k / 2 ( p 0 + p 1 + . . . + p k / 2 − 1 ) sum(p,k/2)+p^{k/2}(p^0+p^1+...+p^{k/2-1}) sum(p,k/2)+pk/2(p0+p1+...+pk/21)
==> s u m ( p , k / 2 ) + s u m ( p , k / 2 ) ∗ p k / 2 sum(p,k/2)+sum(p,k/2)*p^{k/2} sum(p,k/2)+sum(p,k/2)pk/2
==> ( 1 + p k / 2 ) ∗ s u m ( p , k / 2 ) (1+p^{k/2})*sum(p,k/2) (1+pk/2)sum(p,k/2)

k k k为奇数时,那么 k − 1 k-1 k1就是偶数,我们将最后一项提取出来,可得 p k − 1 + s u m ( p , k − 1 ) p^{k-1}+sum(p,k-1) pk1+sum(p,k1)

Code

#include <iostream>
#include <unordered_map>
using namespace std;

typedef long long ll;

const int mod = 9901;

//x^y%mod
ll qpow(ll x,ll y){
    ll ret = 1;
    ll base = x;
    while(y){
        if(y&1){
            ret*=base;
            ret%=mod;
        }
        base*=base;
        base%=mod;
        y>>=1;
    }
    return ret;
}
//sum(p,k) = p^0 + ... + p^(k-1)
int sum(int p,int k){
    if(k==1) return 1;//p^0 = 1
    if(k&1){
        return (qpow(p,k-1)%mod + sum(p,k-1)%mod)%mod;
    }
    //k is even
    return ((1+qpow(p,k/2))%mod*sum(p,k/2)%mod)%mod;
}
unordered_map<int,int> um;
void divide(ll x){
    for(int i=2;i*i<=x;++i){
        if(x%i == 0){
            while(x%i == 0){
                ++um[i];
                x/=i;
            }
        }
    }
    if(x>1) ++um[x];
}
int main(){
    int a,b;
    cin >> a >> b;
    divide(a);
    int p,k;
    ll ans = 1;
    for(auto x : um){
        p = x.first,k=x.second*b;
        ans=(ans%mod*sum(p,k+1)%mod)%mod;
    }
    if(a == 0) ans = 0;
    cout << ans << '\n';
    return 0;
}

思路二、公式法

对于 p 0 + p 1 + . . . + p k p^0+p^1+...+p^{k} p0+p1+...+pk可以使用等比数列求和公式求得一个式子,如下;
p k + 1 − 1 p − 1 \frac{p^{k+1}-1}{p-1} p1pk+11
因为存在模运算,因此需要使用逆元
我们设要模的那个数为 m o d mod mod

判断是否存在逆元

p − 1 % m o d = 0 p-1\%mod=0 p1%mod=0时, p % m o d = 1 p\%mod=1 p%mod=1
因此 ( p 0 + p 1 + . . . + p k ) (p^0+p^1+...+p^{k}) (p0+p1+...+pk)一共有k+1项,对于每个数来说,在模 m o d mod mod下都是1,因此一共就是 k + 1 k+1 k+1;
( p 0 + p 1 + . . . + p k ) % m o d = k + 1 (p^0+p^1+...+p^{k})\%mod=k+1 (p0+p1+...+pk)%mod=k+1

p − 1 % m o d ≠ 0 p-1\%mod\neq0 p1%mod=0时,存在逆元

那么直接使用费马小定理求逆元即可;

不会求逆元请看我这篇博客

Code

#include <iostream>
#include <unordered_map>
using namespace std;

typedef long long ll;

const int mod = 9901;

//x^y%mod
ll qpow(ll x,ll y){
    ll ret = 1;
    ll base = x;
    while(y){
        if(y&1){
            ret*=base;
            ret%=mod;
        }
        base*=base;
        base%=mod;
        y>>=1;
    }
    return ret;
}
unordered_map<ll,ll> um;
void divide(ll x){
    for(int i=2;i*i<=x;++i){
        if(x%i == 0){
            while(x%i == 0){
                ++um[i];
                x/=i;
            }
        }
    }
    if(x>1) ++um[x];
}
int main(){
    ll a,b;
    cin >> a >> b;
    divide(a);
    ll p,k;
    ll ans = 1;
    for(auto x : um){
        p = x.first,k=x.second*b;
        if((p-1)%mod == 0){
            //没有逆元
            ans = (ans%mod*(k+1)%mod)%mod;
        }else{
            ans = (ans%mod*(qpow(p,k+1)-1)%mod*qpow(p-1,mod-2)%mod)%mod;
        }
    }
    if(a == 0) ans = 0;
    //乘了逆元可能出现负数
    //而c++的模运算不一定是正数
    //因此需要这样写
    cout << (ans%mod+mod)%mod << '\n';
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值