POJ 1845 Sumdiv题解(C++ 整数惟一分解定理+分治法求等比数列之和+快速幂)
传送门:
POJ 1845 SumDiv
整数惟一分解定理
任何一个大于1的整数n都可以分解成若干个质因数(素因数)的连乘积,如果不计各个素因数的顺序,那么这种分解是唯一的,即若n>1,一定存在:n=p1p2p3…pm(p1.p2.均为n的质因数)
上式常记作:
称为n的标准分解式,质因数分解式。
对于n的标准分解式来说:根据n的标准分解式可以得到以下结论!!!!很重要!!!
- 可求得n的所有因子个数:(a1+1)(a2+1)(a3+1…+ak+1)
- n的所有因子之和:对每个质因数计算由0次至最高次方幂的和,最后把得到的数相乘就得到n的所有因子的和.
注意:这两个结论非常重要!!!!!!!!!!
再次回到题目:让我们求x^y,因此x的标准分解式如上图所示,我们计算x的y次方,实际上就是计算x的标准分解式的y次方!!!!!!!!!!!
因此题目让我们求x^y的所有因子之和取余9901,因此这道题就是让我们求:
上图中x^y的所有因子之和 然后取余p的结果。,我们只要求得上面的那个长式子的值即可,这个长式子其实就是等比数列求和,因为它的每一个括号里都是一个等比数列的项
我们的基本思路就已经完成了,即我们现在的问题就是如何求得 x^y 的所有因子之和的那一串长式子,即等比数列的和。
可以看出:
- 1+p1^1+ p1^2+ p1^3 +… p1^n
- 1+p2^1+ p2^2+ p2^3 +… p2^n
- 1+pn^1+ pn^2+ pn^3 +… pn^n
他们都具有相同的结构,即
但是如何求出这个等比数列的和呢?
分治法求等比数列和
我们采用分治法求等比数列的和,我们首先来给这个S举一个例子:
因此我们就可以得到S的计算公式,分别为n是奇数的情况和n是偶数的情况。而我们注意到,在Sn的式子中,我们又可以得到(1+p1^1+… +p1^(n/2))这样一个式子,和我们一开始的S的式子形状类似:所以我们便可以通过递归求解中间第二个长式子,而我们可以通过快速幂求得第一个括号里的短式子:(1+ p1^(n/2+1)),而我们要求这一个Sn,只需要传递一个n参数(位数)和p1参数(底数)。
分治求快速幂的模板:注意求和之后要取mod
//分治求快速幂 LL sum(int n,int p1) { if (n == 0)return 1; if (n & 1) { //n是奇数 return (1 + qpow(p1, n / 2 + 1)) * sum(n / 2, p1) % p; } else { //n是偶数 return (1 + qpow(p1, n / 2 + 1)) * (sum(n / 2 - 1, p1)) % p + qpow(p1, n / 2) % p; } }
完整代码
之和,我们便可以求出x的每一个质因子,以及这个质因子所出现的次数,然后带入sum中,求出x^y 的每一个质因子的连乘积,一直乘到x<=1便得到了答案,注意几个细节:
- 代码中出现的所有数据类型最好使用long long 型,因此即使是一个统计某一个质因子的出现的次数 Cnt也应该是一个long long
- 注意所有式子中%p的顺序:在n为奇数的式子最后%p,在n为偶数的式子的中间%p,后面还有一个快速幂的式子也要%p(具体的我也不知道,反正%肯定没坏处)。另外ans在计算每一项式子得到答案的时候要一项一项的乘,然后%p,不能让ans乘以(…%p)的式子,即不能写成: ans*= … 这样写ans会超出数据范围,因为ans本身并没有%p。
#include <iostream> #include <cmath> using namespace std; typedef long long LL; const LL p = 9901; int x, y; LL qpow(LL a, LL b) { /* 快速幂 */ LL res = 1 % p; for (; b; b >>= 1) { if (b & 1) { res = res * a % p; } a = a * a % p; } return res; } //分治求快速幂 LL sum(int n,int p1) { /* n:项数 p1:底数 */ if (n == 0)return 1; if (n & 1) { //n是奇数 return (1 + qpow(p1, n / 2 + 1)) * sum(n / 2, p1) % p; } else { //n是偶数 return ((1 + qpow(p1, n / 2 + 1)) * (sum(n / 2 - 1, p1)) % p + qpow(p1, n / 2) % p); } } int main() { LL ans = 1; cin >> x >> y; //x^y for (int i = 2; i <= sqrt(x); i++) { int cnt = 0; if (x % i == 0) { while (x % i == 0) { x /= i; cnt++; } //i:就是x的某一个质因子 //cnt:就是x的某个质因子的幂次 cnt*y 就是x^y的质因子的连乘积的某一项 } //n的所有因子之和的每一项: ans依次相乘 ans *=sum(cnt * y, i) % p; } if (x > 1) { ans *=sum(y, x) % p; } cout << ans; return 0; }
参考资源:
分治法求等比数列之和
《算法竞赛进阶指南》
本文来自博客园,作者:hugeYlh,转载请注明原文链接:https://www.cnblogs.com/helloylh/p/17209623.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)