二解 约数和

问题描述:给出a和b求a^b的约数和。
输入输出格式
输入格式:一行两个数a,b。
输出格式:一个数表示结果对 9901 的模。
输入输出样例
输入样例#1:2 3
输出样例#1:15
说明:对于 30%的数据,a,b≤ 10 对于 100%的数据,0 ≤ a,b ≤ 50 000 000
分析:此题题面简洁,容易想到,要求约数和,则可以利用约数和公式来暴力解,但是数据规模却明显的告诉你会TLE.那么会超时的地方在哪呢,分析下此题的做题步骤:
方法一:递归求等比数列之和
1.将a分解质因数,a=(p1k1)(p2k2)(p3k3)….(pn^kn)
2.将每个质因数的指数 * b ,a^b =(p1^(k1 * b))(p2^(k2 * b))(p3^(k3 * b))….(pn^(kn * b))
3.根据约数和公式:对于已经分解的整数
有a^b的所有因子之和为
S = (1+p1+p12+p13+…p1^(k1* b)) * (1+p2+p22+p23+….p2^(k2 * b)) * (1+p3+ p3^2+…+ p3^(k3 * b)) * …. * (1+pn+pn2+pn3+…pn^(kn * b))
从上面步骤上来看,可知如果TLE,那一定是在第3步求因子之和上,亲测如果用快速幂并累加和累乘后在luogu上能40分,那么我们仔细观察因子之和的每个乘积因子都是一个等比数列之和,可以利用等比数列公式去计算,但是除法取余需要涉及到逆元【见方法二】,可以利用下图所示的公式递归得出:(此图感谢洛谷用户-恶魔)
这里写图片描述
证明如下图:
这里写图片描述
写到这,说明上面我虽列出3个操作步骤,实际上可以边质因数分解,边把2,3步逐步完成。
代码附上:

#include<iostream>
#include<cmath>
#define MOD 9901 
using namespace std;
int a,b,sum;
int quickMod(int x,int y){//快速幂
	int ans=1;
	x %= MOD;
	while(y){
		if(y&1) ans = (ans * x) % MOD;
		y = y/2;
		x = (x * x)%MOD;
	}
	return ans;
} 
int calc(int p,int c)  //利用奇偶性递归求等比数列求和
{
	if(c == 0) return 1; 
	if(c&1) return ((quickMod(p,(c+1)/2) + 1)%MOD * calc(p,(c-1)/2) % MOD)% MOD;
	else return (((quickMod(p,c/2) + 1) %MOD * calc(p,c/2 - 1)%MOD )%MOD + quickMod(p,c))%MOD;
} 
int solve(){
	int ans = 1,cnt;
	//质因数分解
	for(int i = 2,t = sqrt(a); i<= t; i++)
	{
		if(a%i==0)
		{
			cnt = 0;  //统计i的指数 
			while(a%i==0){
				++cnt;
				a = a/i;
			}	
			int q = cnt * b;
			sum = calc(i,q);   
			ans = (ans * sum)%MOD;
		} 
	} 
	if(a >1){
	        cnt = 1;
			int q = cnt * b;
			sum = calc(a,q);
			ans = (ans * sum)%MOD;
	 } 
	return ans;
 } 
int main(){
	scanf("%d%d",&a,&b);	
	printf("%d",solve());
	return 0;
 } 

方法二:逆元求等比数列求和,其他不变

#include<iostream>
#include<cstdio>
#include<cmath>
#define MOD 9901 
using namespace std;
int a,b,sum;
const int MD = 9901;
typedef long long LL;
LL exgcd(LL a,LL b,LL &x,LL &y)  //扩展欧几里得
{
	if(b==0){
		x = 1;y =0; return a;
	}
	else{
		LL d = exgcd(b,a%b,x,y);
		LL t = x; x = y; y = t - a/b * y;		
		return d;
	}
}
LL inv(LL b,LL n){  //求逆元
	LL x,y;
	LL d = exgcd(b,n,x,y);
	if(d==1) return (x%n + n)%n;
	else return -1;
}
int quickMod(int x,int y){//快速幂
    int ans=1;
    x %= MOD;
    while(y){
        if(y&1) ans = (ans * x) % MOD;
        y = y/2;
        x = (x * x)%MOD;
    }
    return ans;
} 
int solve(){
    int ans = 1,cnt;   //质因数分解
    for(int i = 2,t = sqrt(a); i<= t; i++)
    {
        if(a%i==0)
        {
            cnt = 0;  //统计i的指数 
            while(a%i==0){
                ++cnt;
                a = a/i;
            }   
            int q = cnt * b;
            int q_1 = inv(i-1,MOD) ;   //求i-1的逆元
            sum=((quickMod(i,q+1) - 1) * q_1)%MOD;  //按等比数列求和公式计算和值
            ans = (ans * sum)%MOD;
        } 
    } 
    if(a >1){
            cnt = 1;
            int q = cnt * b;
            LL q_1 = inv(a-1,MOD) ;   //求a-1的逆元
            sum=((quickMod(a,q+1) - 1) * q_1)%MOD; //按等比数列求和公式计算和值
            ans = (ans * sum)%MOD;
     } 
    return ans;
 } 
int main(){
    scanf("%d%d",&a,&b);    
    printf("%d",solve());
    return 0;
 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值