约数之和和约数个数

一个数的约数个数的求法:
对于一个数 x x x,由算术基本定理 (任何一个大于 1 1 1的自然数,都可以唯一的分解成有限个质数的乘积)
x = p 1 a 1 p 2 a 2 … … p k a k x = p_1^{a_1}p_2^{a_2}……p_k^{a_k} x=p1a1p2a2pkak
t = p 1 b 1 p 2 b 2 … … p k b k ( 0 < = b i < = a i ) t = p_1^{b_1}p_2^{b_2}……p_k^{b_k} (0<=b_i<=a_i) t=p1b1p2b2pkbk(0<=bi<=ai)
那么 t t t一定是 x x x的一个约数
所以根据指数的不同, x x x的约数也不同,指数的所有不同取法囊括了 x x x的所以约数
所以: x x x的约数个数为 ( a 1 + 1 ) ( a 2 + 1 ) ( a 3 + 1 ) … … ( a k + 1 ) (a_1+1)(a_2+1)(a_3+1)……(a_k+1) (a1+1)(a2+1)(a3+1)(ak+1)

一个数的约数之和的求法:
由上面可以知道: x = p 1 a 1 p 2 a 2 … … p k a k x = p_1^{a_1}p_2^{a_2}……p_k^{a_k} x=p1a1p2a2pkak
对于其中一个质数 p i p_i pi,它的所有可能个数在 0   a i 0~a_i 0 ai之间
根据母函数的有关知识:
x x x的约数之和为:
( p 1 0 + p 1 1 + … … + p 1 a 1 ) ( p 2 0 + p 2 1 + … … + p 2 a 2 ) … … ( p k 0 + p k 1 + … … + p k a k ) (p_1^0+p_1^1+……+p_1^{a_1})(p_2^0+p_2^1+……+p_2^{a_2})……(p_k^0+p_k^1+……+p_k^{a_k}) (p10+p11++p1a1)(p20+p21++p2a2)(pk0+pk1++pkak)


题目
简单版:
给定n个正整数ai,请你输出这些数的乘积的约数之和,答案对1e9+7取模。

输入格式
第一行包含整数n。
接下来n行,每行包含一个整数ai。

输出格式
输出一个整数,表示所给正整数的乘积的约数之和,答案需对1e9+7取模。
数据范围
1≤n≤100, 1≤ai≤2∗1e9

const int mod = 1e9+7;

int main()
{
	int n; cin>>n;
	
	map<int,int> prime; //得到所有的因子和其次数
	while(n--)
	{
		int x; cin>>x;
		for(int i=2;i<=x/i;i++)
			while(x%i==0)
			{
				prime[i]++;
				x/=i;
			}
		if(x>1)	prime[x]++;
	}
	ll res=1;
	map<int,int>::iterator it; 
	for(it=prime.begin();it!=prime.end();it++) //遍历每一个因子
	{
		int p=it->first,a=it->second;
		ll sum=1;
		while(a--)	sum=(sum*p+1)%mod;
		res=res*sum%mod;
	}
	cout<<res<<endl;
	return 0;
}

困难版:
假设现在有两个自然数A和B,S是AB的所有约数之和。

请你求出S mod 9901的值是多少。

输入格式
在一行中输入用空格隔开的两个整数A和B。

输出格式
输出一个整数,代表S mod 9901的值。

数据范围
0≤A,B≤5×1e7


首先介绍一个求p0+p1+p2+……+pk的快速求法(log级):

设函数sum(p,k) =p0+ p1+p2+……+pk
= (p0+ p1+p2+……+pk/2)+(pk/2+1+……+pk)
当k为奇数时,两边的个数是相等的
= (p0+ p1+p2+……+pk/2)(1+pk/2+1)
= sum(p,k/2)(1+pk/2+1)
如果k是偶数,先算求出来k-1时的结果,然后每一个都乘上p,最后+1
= p*sum(p,k-1)+1;


#include<cstdio>
#include<cmath>
#include<ctime>
#include<cstring>
#include<iostream>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<string>
#include<vector>
#define ll long long
#define ull unsigned long long
#define up_b upper_bound
#define low_b lower_bound
#define m_p make_pair
#define mem(a) memset(a,0,sizeof(a))
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define inf 0x3f3f3f3f
#define endl '\n'
#include<algorithm>
using namespace std;

inline ll read()
{
	ll x=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9')	{ if(ch=='-') f=-1; ch=getchar(); }
	while('0'<=ch&&ch<='9')	x=x*10+ch-'0', ch=getchar();
	return f*x;
}

const int mod = 9901;

int power(int a,int b)
{
    a %= mod;
	int res=1;
	while(b)	{ if(b&1) res=res*a%mod; a=a*a%mod; b>>=1; }
	return res;
}

int sum(int p,int k)
{
	if(k == 0)	return 1;
	if(k & 1)	return sum(p, k / 2) % mod * (1 + power(p, k / 2 + 1)) % mod;
	return (p % mod * sum(p, k - 1) + 1) % mod;
}

int main()
{
	int a,b;
	cin>>a>>b;
	
	if(a==0)    { cout<<0<<endl; return 0; } //特判
	
	int res=1;
	for(int i=2;i<=a;i++)
	{
		int s=0;
		while(a%i==0)
		{
			s++;
			a/=i;
		}
		if(s)	res=res*sum(i,s*b) % mod;
	}
	cout<<res<<endl;
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值