0
点赞
收藏
分享

微信扫一扫

[质因数分解][除法取余]Sumdiv LibreOJ10211

萧萧雨潇潇 2022-02-16 阅读 69
算法

题目描述

原题来自:Romania OI 2002

求 A^B 的所有约数之和 mod 9901。

输入格式

输入两个整数 A,B。

输出格式

输出答案 mod9901。

样例

InputOutput
2 3
15

2^3=8,8 的所有约数为 1,2,4,8,15 mod 9901=15,因此输出 15。

数据范围与提示

对于全部数据,0≤A,B≤5×10^7。

题意: 输出A^B全部因数的和除9901的余数。

分析: 这个问题本身很简单,质因数分解出来,例如分解成num = Πai^bi,全部因数和就是Π(1+ai+ai^2+......+ai^bi)这个乘积式,显然内部是一个等比数列求和,但是众所周知求和时要进行一次除法,虽然模数是质数但分母不一定和模数互质,所以不能用费马小定理求出乘法逆元。

这时有两种选择,第一种是放弃公式求和做法,而是用函数递归一半一半地求和,求出前n/2项和后再乘以q^(n/2)就是后n/2项和,不断向下递归就能得到前n项和,效率也是显然易见的低。

第二种方法其实是除法取余的特殊情况,在b|a的前提下,即a%b == 0时,(a/b)%mod == a%(mod*b)/b,证明如下:

而本题的等比数列求和后一定是整数,所以可以用这个公式,不过大部分情况下b*mod的值会非常大,一般都会大于1e9,在如此大的模数下求快速幂基本都会爆掉long long,所以一些关键值要开__int128。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
using namespace std;

const int MOD = 9901; 
int a, b, p[100005], n[100005], num;
__int128 mod;

__int128 ksm(__int128 base, int power)
{
	__int128 ans = 1%mod;
	base %= mod;
	while(power)
	{
		if(power&1) ans = ans*base%mod;
		base = base*base%mod;
		power >>= 1;
	}
	return ans;
}

signed main()
{
	cin >> a >> b;
	//质因数分解 
	num = 0;//记录不同质因子个数 
	for(int i = 2; i*i <= a; i++)
	{
		if(a%i == 0)
		{
			p[++num] = i;
			while(a%i == 0)
			{
				a /= i;
				n[num]++;
			}
			n[num] *= b;
		}
	}
	if(a > 1)
	{
		p[++num] = a;
		n[num] = b;
	}
	__int128 ans = 1;
	for(int i = 1; i <= num; i++)
	{
		__int128 b = p[i]-1;
		mod = MOD*b;
		ans = ans*((ksm(p[i], n[i]+1)-1+mod)%mod/b)%MOD;
	}
	cout << (int)ans;
    return 0;
}
举报

相关推荐

0 条评论