约数及反素数

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

算数基本定理的推论

       若正整数 N 被分解为 N = p1^{c1}p2^{c2} ...pm^{cm}  , 其中 ci 都是正整数, pi 都是质数 . 

       N 的正约数集合可以写作:   {p1^{b1}p2^{b2} ...pm^{bm} } , 0<=bi <= ci ; 

      N 的正约数个数为  : 

       (c1+1)*(c2+1)*(c3+1) * ... *(c_{m}+1) = \prod_{i=1}^{m}(c_{i}+1)

      N 的所有正约数的和 : 

    (1+p1 +p1^{2} + p1^{3} .. +p1^{c_{1}}) * ..*(1+p_{m} +p_{m}^{2} + p_{m}^{3} .. +p_{m}^{c_{1}}) = \prod_{i=1}^{m}(\sum_{j=0}^{c_{i}} (p_{i})^{j})

求 N 的正约数的集合  -- 试除法

        若 d\geq \sqrt{N} 是 N的约数, 则 N/d \leq \sqrt{N} 也是 N 的约数 ,就是说约数是成对出现的(除了完全平方数, \sqrt{N}  单独出现) 

       因此只需扫描 1 ~\sqrt{N}  . 

const int MAX = 2005  ;
int factor[MAX] ;
int cnt = 0 ;
void divisors(int n){
	for(int i = 1 ; i*i <=n ; i++ ) {
		if(n%i == 0 ){
			factor[++cnt] = i ; // 如果能整除就是约数
			if(i!=n/i){ // 考虑成对的另一个 , 完全平方除外
				factor[++cnt] = n/i ;
			}
		}
	}
}

试除法的推论  

            一个整数 N 的约数个数的上限为 2\sqrt{N}

 

求 1 ~ N 每个数的正约数集合 -- 倍数法

复杂度O(NlogN)

vector<int> fac[MAX] ;
void Divisors(int n){
	for(int i = 1 ; i<=n ; i++ ) {
		for(int j = 1 ; j<=n/i ;j++ ) {
			fac[i*j].push_back(i) ;
		}
	}
	for(int i = 1 ; i<=n ; i++){
		for(int j = 0 ; j<(int)fac[i].size() ;j++ ){
			cout<<fac[i][j]<<" " ;
		}
		cout<<endl ;
	}
	
	
}

倍数法的推论

1 ~ N 的每个数的约数个数总和大约为 N log N 。

[例题] 反素数

  定义 : 

           对于任何正整数x,其约数的个数记作g(x)。例如g(1)=1、g(6)=4。

           如果某个正整数x满足:g(x)>g(i) 0<i<x,则称x为反质数。例如,整数1,2,4,6等都是反质数。

            或者说 反素数就是区间内约数个数最多的那个数。根据题目要求,如果有多个满足,选取最大的一个 . 

         现在给你一个数 N ( 1 \leq N \leq 2*10^{9})  求出不超过 N 的最大的 反质数 . 

   引理 1 : 

            1 ~N 中最大的反质数,就是 1 ~ N 中约数个数最多的数中最小的一个 . 

 

  引理 2 : 

            1~ N 中任何数的不同质因子都不会超过10 个(本题范围给出), 且所有质因子的指数总和不超过 30 . 

           简单证明: 最小的11 个质数的乘积已经大于 2 * 10^9 

  引理 3 : 

           对于 x 属于 [1,N] , x 为反质数的必要条件是 : x 分解质因数后可以写作2^{c_{1} } * 3^{c_{2}} * 5^{c_{3}}*7^{c_{4}}*11^{c_{5}}*13^{c_{6}} ...*29^{c_{10}} 

            且 ci 单调递减. 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <queue>
#define inf 2e9
using namespace std ;
const int MAX = 2005  ;
typedef long long LL ;
LL prime[11] = {0,2,3,5,7,11,13,17,19,23,29} ;
LL c[11] ,ans = inf ;
LL n ;
LL cnt_ans = 1 ;
// 1 1  1
void dfs(LL now , LL num , LL cnt ) {
	// now 当前枚举到第几个素数了 , num 当前的值  , cnt 当前num因子的数量
	cout<<now <<" "<<num<<" "<<cnt<<endl ;
	if(now == 11 ){
		// 如果当前约数的个数比以前都多, 或者是和之前相等,但是数值比以前小
		// 更新
		if(cnt > cnt_ans || (cnt_ans == cnt && ans >num)){
			ans = num ;
			cnt_ans = cnt ;
		}
		return  ;
	}
	LL num_cnt = num ;
	// 枚举指数
	
	for(int i = 0 ;i<=c[now-1] ; i++ ){
		if(num_cnt >n){ // 如果当前的值比 n 大了直接返回
			return ;
		}
		c[now] = i ;
		//  n 的 正约数之和为 (c1+1) *(c2+1) .. *(cm+1)
		dfs(now+1,num_cnt,cnt*(i+1)) ;
		num_cnt*=prime[now] ;
	}
}
int main(){
	cin >> n ;
	c[0] = inf ;
	// 从素数2 开始枚举;  当前值为1这个值是由若干个素数乘积组成 ; 指数个数为1 也就是 2^1
	// 第一个反素数是 2
	dfs(1,1,1) ;
	cout<<ans ;
    return 0 ;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值