洛谷1593-幂次方因子和-C++-(快速幂+乘法逆元+等比数列+分解质因数)

题目描述
输入两个整数 a 和 b,求 a^b的因子和。
a,b的范围都在在10^7以内。
由于结果太大,只要输出它对 9901 取模的结果。

对于这个题目来说,我们最直观的解法是找出a^b的所有的因子,相加之后取模,得到答案。
但是观察一下数据的范围,假设a,b都是非常大的数,直接计算得到结果,再进行分解因式得到因子和,这显然是不现实的一件事。
比如说我们取a=10^6, b=10^6,那么 a^b将会是一个天文数字,绝对会数据溢出,所以果断放弃这种做法。

我们想到质因数分解定律,一个大于1的整数可以分解成若干质数的幂的乘积。假设一个数为M,则M=p1^k1 * p2^k2 pn^kn。其中p1,p2.,…,pn是质数,k1,k2,…,kn是幂次。

举一个例子,假设这个数为36,那么36=4*9=2^2 ×3^2,那么2,3就是质数,次数都是2。对于36而言,如何找出它的因子和呢?
首先我们找出它的因子,因子就是取不同次幂时的结果的和。如何理解。
就是2的次幂是2,3的次幂是2。所以次幂可以取0 1 2,这样就有3*3=9个因子。分别是2取0次幂,3取0 1 2次幂,得到的结果是1 3 9;2取1次幂,3取0 1 2次幂,得到的结果是2 6 18;2取2次幂,3取0 1 2次幂,得到的结果是4 12 36。经过验证可以得出这些结果是正确的。因子和就是1+3+9+2+6+18+4+12+36
应用组合思想因子和可以写成:(1 + 2 + 4)×(1 + 3 + 9)

从样例情况推导至一般情况:
那么因子和就可以写成:
(1+p1+p1^2 + …+p1^k1 )×…×(1+pn+pn^2 +…+pn^kn)

由于是计算次幂,a^b结果可以写成如下:
(1+p1+p1^2 + …+p1^k1*b )×…×(1+pn+pn^2 +…+ pn^kn *b),只不过在每一项的幂次扩大了b倍。
至此我们得出了因子和的表达式

我们通过观察可以得出,这个结果的每一项都是等比数列,根据等比数列的求和公式得出每一项的结果应该是( p^(kb+1)-1) / (p-1)。由于结果要对9901取模,这里引出取模的一些性质。
取模运算的性质:
a * b %mod=(a%modb%mod)%mod
(a±b)%mod=(a%mod±b%mod)%mod
对于除法而言,在保证整除的情况下(a/b)%mod并不等于(a%mod)/(b%mod),但是等于a * c%mod,然后乘法又满足第一条性质。
其中c是b在模mod意义下的乘法逆元
即b
c≡1(mod p),注意这个p不要与上面的公比p混淆,这个p是模的大小,关于乘法逆元的定义以及应用可以自己在Bzhan上搜寻视频,有很清楚的讲解。

之后,我们对于每一项的等比数列结果( p^(kb+1)-1) / (p-1)%mod,可以写成( p^(kb+1)-1) *c%mod=(( p^(kb+1)-1) %mod * c%mod)%mod。我们为什么要写成乘法的形式?

为什么结果一定要写成乘法的形式?
关键就是我们等比数列结果的分子部分我们要得到确切的结果,然后除以p-1得到最终结果,再对mod取余,但是分子部分的结果是有可能会溢出的,极其可能溢出,所以我们写成乘法的形式。这样我们并不用计算出分子实际的结果,直接取模就可以,这样会十分方便。因为乘法的取模性质。

如何计算p-1的乘法逆元
利用费马小定理
费马小定理
如果模p与a互质,那么a^(p-1)≡1(mod p),为了不与公比中的p混淆,我们把公比写成q。那么,a*a^(p-2)≡1(mod p),我们把a换成q-1,那么逆元就是(q-1)^(p-2)。

经过上面的分析,我们已经解决了90%的问题,剩下的就是计算幂次方,幂次方的计算我们可以通过快速幂来计算,快速幂算法在之前有讲到过。

考虑一种情况,p与a不互质:
由于p现在是一个质数,如果不互质的话,那么a就只能是p的倍数,即q-1%p=0,于是q%p=1,那等比数列将会变成1%p+q%p+(q%p)^2+…+(q%p) ^kb=1+kb,非常容易计算。
至此,题目的分析已经完成,接下来就是代码的实现

#include<iostream>
using namespace std;
int a,b,index,ans=1;
int mod=9901;
int son[100001][2];
int FastPow(int a,int b){
	int value=1;
	while(b){
		if(b&1)
			value=(value*(a%mod))%mod;
		a=((a%mod)*(a%mod))%mod;
		b=b>>1;
	}
	return value;
}

//分解质因数
void split(int a){

	//如果当前这个数是质数 那么a最后不会等于1
	//如果当前这个数是合数 那么一个因子一定小于根号a
	for(int i=2;i*i<=a;i++){
		if(a%i==0){
			a=a/i;
			son[index][0]=i;
			son[index][1]=1;
			while(a%i==0){
				a=a/i;
				son[index][1]+=1;
			}
			index++;
		}
	}

	if(a!=1){
		son[index][0]=a;
		son[index][1]=1;
		index++;
	}
}

//获取等比数列的结果
int getValue(int q,int len){
	int n=len*b+1,value=0;
	if(q%mod==1){
		value=n;
	}
	else{
		value=((FastPow(q,n)-1)*FastPow(q-1,mod-2))%mod;
	}
	return value;
}

//等比数列求解q-1的逆元 等比结果的除法求余可以变成乘法求余 简化运算
//如果 q-1与p互质 则利用费马小定理
//如果 q-1与p不互质 由于p已经是一个质数了 所以q-1是p的倍数 等比数列变成1+1^1+1^2+...+1^k*p=1+kp

int main(){
	scanf("%d %d",&a,&b);
	if(a==0){
		printf("%d",0);
		return 0;
	}
	split(a);

	for(int i=0;i<index;i++)
		ans=(ans*getValue(son[i][0],son[i][1]))%mod;
	printf("%d",(ans%mod+mod)%mod);
	return 0;
}

AC了
在这里插入图片描述
这道题的综合性还是比较强的,不愧是省选题:)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值