Project-Euler-048快速乘防爆longlong

048题:

题意:

​ 十项的自幂级数求和为 1^1 + 2^2 + 3^3 + … + 10^10 = 10405071317。

​ 求如下一千项的自幂级数求和的最后10位数字:1^1 + 2^2 + 3^3 + … + 1000^1000

思路:

​ 设mod为1e10,根据同余定理,我们可以利用快速幂求i^i关于mod的模。但是有一个问题,就是快速幂中存在两数先相乘再取余mod的过程,可能两数在相乘时就爆long long了。因此,我们可以通过一个小技巧来防治爆long long。

​ 对于a * b % mod,我们设k为1e5,那么一定存在a = ik + j,b = pk + q (其中i = a / k,j = a % k,p = b / k,q = b % k)

​ 把a和b带入原式,得到:

a * b % mod = (ik + j) * (pk + q) % mod
			= (ipk^2 + iqk + jpk + jq) % mod

​ 由于ipk^2,iqk,jpk,jq都在longlong范围内,因此解决了爆longlong问题

代码:

#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#define d int32_t
#define r return
#define ll int64_t
#define t (ll)1e5
#define mod (ll)1e10
#define For(i, star, endd) for(d i = star; i <= endd; i++)

//乘法防爆longlong
ll mul (ll a, ll b) {
    ll ans = 0;
    ll i = a / t;
    ll j = a % t;
    ll p = b / t;
    ll q = b % t;
    ans = (i * p % mod * t % mod *t % mod + i * q % mod * t % mod + j * p % mod * t % mod + j * q % mod) % mod;
    r ans;
}

//快速幂
ll quick(ll a, ll b) {
    ll ans = 1;
    while (b) {
        if (b & 1) ans = mul(ans, a);
        b >>= 1;
        a = mul(a, a);
    }
    r ans;
}

//循环球i^i
ll work() {
    ll ans = 0;
    For(i, 1, 1000) {
        ans = (ans + quick(i, i)) % mod;
    }
    r ans;
}

d main()
{
    ll ans = work();
    printf("%" PRId64 "\n", ans);
    r 0;
}

最后结果为:9110846700

转载请注明出处!!!

如果有写的不对或者不全面的地方 可通过主页的联系方式进行指正,谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值