洛谷 [P1593 因子和] {快速幂+费马小定理求逆元+求解质因子} 奋斗的珂珂~

题目描述

输入两个整数 a 和 b,求 a b a^b ab 的因子和。

由于结果太大,只要输出它对 9901 取模的结果。

输入格式

仅一行,为两个整数 a和 b。

输出格式

输出一行一个整数表示答案对 9901 取模的结果。

输入输出样例

输入 #1复制
2 3
输出 #1复制
15

说明/提示

数据规模与约定

对于全部的测试点,保证 1≤a≤5× 1 0 7 10^7 107,1≤b≤5× 1 0 7 10^7 107

解题思路

1、{分解质因子}
首先,看到题目要求的数据,就已经确定是无法使用暴力的,那么根据以往的经验,我们就要求解整数a的质因子,并求解出每个质因子的个数,然后再让该质因子的个数乘以b就是对于 a b a^b ab这个数中分解出来的每一个质因子的个数。

eg:
4 1 4^1 41中 质因子2的个数就是2*1=2个
4 2 4^2 42中 质因子2的个数就是 2 ∗ 2 2*2 22=4个
4 3 4^3 43中 质因子2的个数就是 2 ∗ 3 2*3 23=6个

(本题使用数组进行存储即可)

2、{等比数列求和}
     然后我们对求解出的质因子进行分析,不难发现,假设当前质因数为m,质因数的个数为n,那么 m n m^n mn,肯定 a b a^b ab的一个因子而且是以m为底数的最大因子。

eg:
16能分解的因子分别是:1,2,4,8,16.
由上述思路1可知16的质因子的个数是4,也就是我们以2为底数最大的该数的因子就是 2 4 2^4 24=16,与此同时,我们可以发现16的因子和就等于=1+ 2 1 2^1 21+ 2 2 2^2 22+ 2 3 2^3 23+ 2 4 2^4 24

也就是说对于 a b a^b ab可以分解的质因数,这个数因子和的一部分就等于m从0到n次幂的和。

综上所述,我们求解该数的因子和就等于(借图一用,感谢!)
在这里插入图片描述
这里的pi就相当于可以拆解的质因子,ci就代表该数可以分解得到的当前之因子的个数。

   
看到这里,结合高中的知识,就将问题转化为等比数列求和。
等比数列求和公式:
在这里插入图片描述
在本题中a1均为1,q代表质因子的值,n代表每一个质因子的个数。

将相关数据代入 q=m(质因子的值),n(等比数列的项数)=n(质因子的个数)+1。进而推出:
在这里插入图片描述
看到上面的 p i c i + 1 pi^{ci+1} pici+1我们就会想到快速幂,直接套用模板就可以。

3、{费马小定理求逆元}
最后,我们看题中所给的条件,题目中让我们对结果数对9901进行取余,为什么要对这么特别的数字进行取余呢?可以发现9901是一个素数,再观察上面等比数列求和的公式,发现我们还有 /(pi-1)没有进行处理,这时就要想到求逆元。

在这里插入图片描述
也就是说除以一个数,换做乘以这个数的倒数的结果是不变的,再根据费马小定理的公式有:
在这里插入图片描述
带入公式求解快速幂即可。

注意

1、首先,为了防止取模的时候是负数,可以先将其+mod再进行取模。

2、为什么求解等比数列求和的每一次求解第一项都是1而不是m(m代表当前质因子)?
这也是我最初没有想明白的地方。后来找到了题解中的这一部分:(为大佬点赞!!!)在这里插入图片描述
3、为什么要考虑质因子 (m%9901)==1是否成立?
    因为如果有个质因子m,m%9901=1,那用费马小定理求逆元的时候,(m-1)%9901=0,这时候不存在逆元(0是没有倒数的),所以会出错。那么试想,我们m%9901=1,那式子(1+m+ m 2 m^2 m2+…+ m c n t m^{cnt} mcnt) 是不是就等于cnt+1?(因为里面每一项 m i m^i mi(0<=i<=n)mod 9901之后都是1),所以计算需要特判!!!

完整代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=9901;
int yin[10010][2];//0下标对应因子,1下标代表该因子的个数
int cnt=0;
 

ll qpow(ll a,ll b)//矩阵快速幂 
{
	ll res=1;
	while(b)
	{
		if(1&b) res=((ll)a*res)%mod;
		a=(ll)a*a%mod;
		b>>=1;
	}
	return res;
 }
 
int main()
{
	ll m,n;
	scanf("%lld %lld",&m,&n);
	
	for(int i=2;i<=m/i;i++)//求解该数的质因子 
	{
		if(m%i==0)
		{
			while(m%i==0)
			{
				m/=i;	
				yin[cnt][1]++;
			}
			yin[cnt][0]=i;
			yin[cnt][1]*=n;//因为是要求m的n次幂,所以我们在此处因子的个数时也要乘以相应的倍数 
			cnt++; 
		}	
	}
	if(m>1)//该质因子大于根号n 
	{
		yin[cnt][0]=m;
		yin[cnt][1]=n;
		cnt++;//为了下面的循环,必须要让在循环数组的下标小于cnt,因此需要进行++ 
	 } 
	
	ll ans=1;
	for(int i=0;i<cnt;i++)
	{   //前半部分求解的是等比数列求和,为了防止是负数,要加上mod在进行取余 
		ll t=(qpow(yin[i][0],yin[i][1]+1)+mod-1)%mod*qpow(yin[i][0]-1,mod-2)%mod;//第二部分是使用费马小定理求解q-1的逆元 
		if(yin[i][0]%mod==1)//如果说因子对9901取余的余数是1,那么该因子-1对9901取余的结果就是0,那么此时就不存在逆元,此时就变成因子个数+1个1相加 
		{
			ans=ans*(yin[i][1]+1)%mod;
		}
		else ans=ans*t%mod;
	 } 
	printf("%lld",ans);
	return 0;
 } 



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值