定义: 若整数n除以整数d的余数是0,即d能整除 n ,则称 d 是 n 的约数, n是d的倍数,记为 d|n 。
算数基本定理的推论
若正整数 N 被分解为 , 其中 ci 都是正整数, pi 都是质数 .
N 的正约数集合可以写作: { } , 0<=bi <= ci ;
N 的正约数个数为 :
N 的所有正约数的和 :
求 N 的正约数的集合 -- 试除法
若 是 N的约数, 则 N/d 也是 N 的约数 ,就是说约数是成对出现的(除了完全平方数, 单独出现)
因此只需扫描 1 ~ .
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 的约数个数的上限为
求 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 ( ) 求出不超过 N 的最大的 反质数 .
引理 1 :
1 ~N 中最大的反质数,就是 1 ~ N 中约数个数最多的数中最小的一个 .
引理 2 :
1~ N 中任何数的不同质因子都不会超过10 个(本题范围给出), 且所有质因子的指数总和不超过 30 .
简单证明: 最小的11 个质数的乘积已经大于 2 * 10^9
引理 3 :
对于 x 属于 [1,N] , x 为反质数的必要条件是 : x 分解质因数后可以写作
且 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 ;
}