约数相关问题

1.试除法求约数 O(√n)

一个数的约数一定是成对出现的,所以只要判断较小的约数,枚举到 d<√n即可

void get_divisors(int n)
{
	vector<int> v;
	for(int i=1;i<=n/i;i++)
	{
		if(n%i==0)
		{
			v.push_back(i);
			if(i!=n/i)//把更大的那个约数加进来
			v.push_back(n/i);
		}
	}
	sort(v.begin(),v.end());
	for(int i=0;i<v.size();i++)
	cout<<v[i]<<" ";
	cout<<endl;
}

时间复杂度分析:O(√n+mlogm),m是约数个数,上述代码只会循环到√n,实际上约数的个数很小,大概logn个,所以排序的时间基本可以忽略。
约数个数:1 ~ n 中,每个数都是它倍数的约数,1 ~ n 中总共约数的个数等于总共倍数的个数
所以 对1来说它有n个倍数,对2来说它有n/2个倍数,…,对n来说它有1个倍数,所以约数总个数为n*lnn=>nlogn,所以平均来看,对每个数来说,它的约数个数就为logn
所以排序的时间复杂度为logn loglogn(代入nlogn),所以后面半部分其实可以忽略。

做数论题目一定要注意每一步 计算时间复杂度,只有所有都不超才可以
2.约数个数

若整数 n除以整数 d的余数为 0,即 d能整除 n,则称 d是n的约数 ,n是d的倍数,记为 d|n 。

唯一分解定理(算数基本定理): 任一大于 1的 自然数 𝑁,都可以唯一分解为有限个素数之积: N = P 1 c 1 P 2 c 2 . . . P r c r N=P_{1}^{c_1}P_{2}^{c_2}...P_{r}^{c_r} N=P1c1P2c2...Prcr

N的正约数集合可写作: { p 1 b 1 p 2 b 2 . . . p m b m } \left\{p_{1}^{b_1}p_{2}^{b_2}...p_{m}^{b_m}\right\} {p1b1p2b2...pmbm},其中 0 ⩽ b i ⩽ c i 0\leqslant b_i\leqslant c_i 0bici

N的正约数个数 ( c 1 + 1 ) ∗ ( c 2 + 1 ) ∗ . . . ∗ ( c m + 1 ) = Π i = 1 m ( c i + 1 ) \left( c_1+1 \right) *\left( c_2+1 \right)*...*\left( c_m+1 \right) =\varPi_{i=1}^{m}\left( c_i+1 \right) (c1+1)(c2+1)...(cm+1)=Πi=1m(ci+1) 还要包括0,即不选这个质因子的情况,所以是0~c_i种选法,数量为c_i+1,再通过乘法原理可得总个数

在int范围内,约数个数最多的数,最多有1500个左右约数

例题

在这里插入图片描述

输入
3
2
6
8
输出
12
思路

把每个 a i a_i ai都分解质因数,将质因数和它的指数建立映射关系,可以用哈希表或map存下来,这样后续的就只需要增加指数即可。
注意:这里可能会有2*109个数,开全局数组最多只能106或107左右,所以这里没办法用数组来存,我们选用unordered_map来存。

非频繁的查询用map比较稳定;频繁的查询用hash_map效率会高一些,c++11中的unordered_map查询效率会更高一些但是内存占用比hash_map稍微大点。unordered_map 就是 boost 里面的 hash_map 实现。

优点:
因为内部实现了哈希表,因此其查找速度非常的快
缺点:
哈希表的建立比较耗费时间

STL——map/unordered_map基础用法
unordered_map的使用

#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <vector>
const int maxn=105;
const int mod=1e9+7;
using namespace std;
int main()
{
    int n;
    cin>>n;
	unordered_map<int,int> primes;

    while(n--)
	{
		int x;
		cin>>x;
		for(int i=2;i<=x/i;i++)
		{
			while(x%i==0)
			{
				x/=i;
				primes[i]++;
			}
		}
		if(x>1)
		primes[x]++;
	}
	long long ans=1;
	unordered_map<int,int>::iterator it;
	for(it=primes.begin();it!=primes.end();it++)
		ans=ans*(it->second+1)%mod;//这里是先乘再取模,不能写作ans*=(it->second+1)%mod,因为这样会先取模再乘到ans上
	cout<<ans<<endl;
    return 0;
}

注意:ans可能会溢出(ans取模后会在int范围内,但ans*it->second不一定),要用long long来存,long long 大约9*1018

3.约数之和
N的所有正约数之和为:

( 1 + p 1 + p 1 2 + . . . + p 1 c 1 ) ∗ ( 1 + p 2 + p 2 2 + . . . + p 2 c 2 ) ∗ . . . ∗ ( 1 + p m + p m 2 + . . . + p m c m ) = Π m i = 1 ( Σ j = 0 c i ( p i ) j ) \left( 1+p_1+p_{1}^{2}+...+p_{1}^{c_1}\right) *\left( 1+p_2+p_{2}^{2}+...+p_{2}^{c_2} \right) *...*\left(1+p_m+p_{m}^{2}+...+p_{m}^{c_m} \right)=\underset{i=1}{\overset{m}{\varPi}}\left(\varSigma _{j=0}^{c_i}\left( p_i\right) ^j \right) (1+p1+p12+...+p1c1)(1+p2+p22+...+p2c2)...(1+pm+pm2+...+pmcm)=i=1Πm(Σj=0ci(pi)j) 1是 p i 0 p_i^0 pi0
乘法分配律展开之后, = ( A ) + ( B ) + ( C ) + . . . + =(A)+(B)+(C)+...+ =(A)+(B)+(C)+...+,其中A=在上式每一个括号中取一个相乘,取法有 C c 1 + 1 1 ∗ C c 2 + 1 1 ∗ C c 3 + 1 1 + . . . + C c m + 1 1 C_{c_1+1}^1*C_{c_2+1}^1*C_{c_3+1}^1+...+C_{c_m+1}^1 Cc1+11Cc2+11Cc3+11+...+Ccm+11种(就是上式约数个数公式)。

接上例
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <vector>
const int maxn=105;
const int mod=1e9+7;
using namespace std;
int main()
{
    int n;
    cin>>n;
	unordered_map<int,int> primes;

    while(n--)
	{
		int x;
		cin>>x;
		for(int i=2;i<=x/i;i++)
		{
			while(x%i==0)
			{
				x/=i;
				primes[i]++;
			}
		}
		if(x>1)
		primes[x]++;
	}
	long long ans=1;
	unordered_map<int,int>::iterator it;
	for(it=primes.begin();it!=primes.end();it++)
	{
		long long p=it->first,c=it->second;
		long long t=1;
		while(c--)//次数一直减1
		{
			t=(t*p+1)%mod;
		}
		ans=ans*t%mod;
	}
	cout<<ans<<endl;
    return 0;
}

t=1;
t=p*t+1;
t=p*(p*t+1)+1=p^2*t+p+1
...
t=p^c+...+1

这里求和可以不用快速幂,快速幂的时间复杂度为log(k)k是指数,如果有n项,时间复杂度的为O(nlogn),而以上求法为O(n)的,长度如果为n就只需要求n次,如果用分治去求的话,可以到log c,c为指数。

4. 求最大公约数 欧几里得算法(辗转相除法) O(logn)
核心原理:d|a,d|b,d|(ax+by) (a,b)=(b,a%b)

a % b = a − ⌊ a b ⌋ ∗ b , ( a , b ) = ( b , a − c ∗ b ) = ( b , a − c ∗ b + c ∗ b ) = ( b , a ) a\%b=a-⌊\frac{a}{b}⌋*b,(a,b)=(b,a-c*b)=(b,a-c*b+c*b)=(b,a) a%b=abab(a,b)=(b,acb)=(b,acb+cb)=(b,a)
d ∣ a , d ∣ b , d ∣ a − c ∗ b , d ∣ a − c ∗ b + c ∗ b d|a,d|b,d|a-c*b,d|a-c*b+c*b dadbdacbdacb+cb

递归结束条件:(a,0)=a (0%x=0)

int gcd(int a,int b)
{
	return b?gcd(b,a%b):a;//b==0时,最大公约数是a
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值