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便得到了答案,注意几个细节:

  1. 代码中出现的所有数据类型最好使用long long 型,因此即使是一个统计某一个质因子的出现的次数 Cnt也应该是一个long long
  2. 注意所有式子中%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;
}

参考资源:

分治法求等比数列之和
《算法竞赛进阶指南》

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
POJ3635是一道经典的数学题,需要使用一些数学知识和算法进行解决。 题目描述: 给定四个正整数 a、b、p 和 k, a^b^p mod k 的值。 解题思路: 首先,我们可以将指数 b^p 写成二进制形式:b^p = c0 * 2^0 + c1 * 2^1 + c2 * 2^2 + ... + ck * 2^k,其中 ci 为二进制数的第 i 位。 然后,我们可以通过快速幂算法来计算 a^(2^i) mod k 的值。具体来说,我们可以用一个变量 x 来存储 a^(2^i) mod k 的值,然后每次将 i 加 1,如果 ci 为 1,则将 x 乘上 a^(2^i) mod k,最后得到 a^b^p mod k 的值。 代码实现: 以下是 Java 的代码实现: import java.util.*; import java.math.*; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); BigInteger a = sc.nextBigInteger(); BigInteger b = sc.nextBigInteger(); BigInteger p = sc.nextBigInteger(); BigInteger k = sc.nextBigInteger(); BigInteger ans = BigInteger.ONE; for (int i = 0; i < p.bitLength(); i++) { if (b.testBit(i)) { ans = ans.multiply(a.modPow(BigInteger.ONE.shiftLeft(i), k)).mod(k); } } System.out.println(ans); } } 其中,bitLength() 函数用于获取二进制数的位数,testBit() 函数用于判断二进制数的第 i 位是否为 1,modPow() 函数用于计算 a^(2^i) mod k 的值,multiply() 函数用于计算两个 BigInteger 对象的乘积,mod() 函数用于计算模数。 时间复杂度: 快速幂算法的时间复杂度为 O(log b^p),其中 b^p 为指数。由于 b^p 的位数不超过 32,因此时间复杂度为 O(log 32) = O(1)。 总结: POJ3635 是一道经典的数学题,需要使用快速幂算法解。在实现时,需要注意 BigInteger 类的使用方法,以及快速幂算法的细节。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yuleo_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值