本文已参与「新人创作礼」活动,一起开启掘金创作之路。
异想之旅:本人原创博客完全手敲,绝对非搬运,全网不可能有重复;本人无团队,仅为技术爱好者进行分享,所有内容不牵扯广告。本人所有文章仅在CSDN、掘金和个人博客(一定是异想之旅域名)发布,除此之外全部是盗文!
洛谷 P1593 因子和
题目描述
输入两个整数 和 ,求 的因子和。
由于结果太大,只要输出它对 取模的结果。
输入格式
仅一行,为两个整数 和 。
输出格式
输出一行一个整数表示答案对 取模的结果。
输入输出样例
In 1:
2 3
Out 1:
15
数据范围
对于全部的测试点,保证 ,。
题解
为了完成洛谷P1593这道题也是拼了……用到了三个不会的知识:因子和公式和等比数列求和公式和费马小定理求分数
这里另摆一份很好的题解:洛谷 [P1593 因子和] {快速幂+费马小定理求逆元+求解质因子} 奋斗的珂珂~ - 代码先锋网
先摆出因子和公式的参考资料:
等比数列定义很简单,设有数列 ,存在一个定值 使得任意 都有 。由此定义可知 。
这个数列的求和公式为 ,下面我们来证明这个公式:
(参考资料:等比数列公式及推导_高三网)
费马小定理求分数快速幂的公式及证明:
(来源:分数取模(费马小定理)_give it a try-CSDN博客_分数取模运算)
根据整数的唯一分解定理,整数a进行质因数分解对应的式子唯一,有:
又因为本题要分解的是,所以上面的式子又可以写成这样:
证明很简单,就是把上面第一个式子乘上 次即可得第二个式子
接下来我们要求的是因子和,所以就有:
于是求和转化成了等比数列的和的乘积
而对于每一个等比数列,可根据公式求和: (上面推得的等比数列求和公式中代入 ,自然有了 )
注意这里的 与上面截图中的 含义不一样!这里指的是公比(类比等差数列的公差),上面指的是 mod (快速幂中取余的那个数)
有了这些知识来看代码:
#include <bits/stdc++.h>
using namespace std;
const int mod = 9901;
int ys[50000000];
// 一个不那么简单的快速幂模板
long long ksm(long long x, long long y) {
if (y == 0) return 1;
if (y % 2 == 0) return ((long long)pow(ksm(x, y / 2) % mod, 2) % mod);
return (x * (long long)pow(ksm(x, y / 2) % mod, 2) % mod);
}
int main() {
int a, b;
cin >> a >> b;
int aa = a; // 由于aa的值会被修改,所以需要一个变量保存
ys[2] = 0;
while (a % 2 == 0) {
a /= 2;
ys[2]++;
}
ys[2] *= b; // 因为是b次幂因此要乘以b
for (int i = 3; i <= a / i; i += 2) {
ys[i] = 0;
while (a % i == 0) {
a /= i;
ys[i]++;
}
ys[i] *= b;
}
if(a != 1) {
// 分解质因数,若有质因数超过根号a,则只能是a本身
ys[a] = b;
}
long long ans = 1;
for (int i = 2; i <= aa; i++) {
if (!ys[i]) continue; // 不存在该质因数
int temp;
if (i % mod == 1) {
temp = (ys[i] + 1) % mod; // 详见博文最后的解释
} else
temp = ((ksm(i, ys[i] + 1) - 1 + mod) % mod) * ksm(i - 1, mod - 2) % mod; // 使用费马小定理求解
if (temp != 0) ans = ans * temp % mod;
}
cout << ans << endl;
return 0;
}
关于代码第42行特判 i % mod == 1 (也就是判断 i-1 是否为 mod 的倍数;同样适用于45行 temp != 0 特判):
当 i-1 为 mod 的倍数时,等比数列中每一项取余 的结果都为 ,等比数列的和便是 ,因此此处特判改为 temp = ys[i] + 1 ;同理,若 i 为 mod 的倍数,则 temp 值为 恒成立(洛谷测试点中不存在此极端情况因此不考虑也能AC)