ACM里的反素数问题
2015-10-05 22:48
465 查看
定义
对于正整数 x ,其约数的个数记做 g(x) 。例如 g(1) = 1,g(6) = 4.如果某个正整数x满足:
对于任意 i(0<i<x) , 都有 g(i)<g(x) , 则称x为反素数·
反素数的前20项是:
1, 2, 4, 6, 12, 24, 36, 48, 60, 120, 180, 240, 360, 720, 840, 1260, 1680, 2520, 5040, 7560
对应的 g(n) 是:
1, 2, 3, 4, 6, 8, 9, 10, 12, 16, 18, 20, 24, 30, 32, 36, 40, 48, 60, 64
应用
相关问题1.
给定一个数 n,求一个最小的正整数,使得的约数个数为 n例:codeforce 27E
思路:
由算术基本定理定理我们知道:若一个数 x=pa11pa22⋯pass ,
那么 n 的约数个数 g(x)=(a1+1)(a2+1)⋯(as+1)
例如:12=22∗3
图片来自 acdreamer大神
这棵树除了第一层外,每一层对应着一个素数,从上到下递增;每一层的每一个节点对应着素数的幂,从左到右递增,每一条从根节点到叶节点的路径上数字相乘即为一个约数。
我们要想获得 g(x)=n 的最小正整数,就要求x的质因子尽可能小,且尽可能多,换而言之就是幂次尽可能为1,所以就要对上面的树进行从上到下从左到右的dfs直到约数个数满足 n 。
代码:
#include<iostream> using namespace std; typedef unsigned long long lint; int p[] = { 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 , 23 , 29 , 31 , 37 , 41 , 43 , 47 , 53 } ; const lint inf = 1e18 + 5 ; lint ans ; int n ; void dfs( int dept , lint tmp , int num ){ if( num > n ) return ; if( num == n && ans > tmp ) ans = tmp ; for( int i = 1 ; i <= 63 ; i++ ){ if( ans / p[dept] < tmp ) break ; tmp *= p[dept] ; dfs( dept + 1 , tmp , num*(i+1) ) ; } } int main(){ while( cin >> n ){ ans = inf ; dfs( 0 , 1 , 1 ) ; cout << ans << endl ; } return 0; }
当然也可以进行一些剪枝,不过这题没什么影响:
#include<iostream> using namespace std; typedef unsigned long long lint; int p[] = { 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 , 23 , 29 , 31 , 37 , 41 , 43 , 47 , 53 } ; const lint inf = 1e18 + 5 ; lint ans ; int n ; void dfs( int dept , int limit , lint tmp , int num ){ if( num > n ) return ; if( num == n && ans > tmp ) ans = tmp ; for( int i = 1 ; i <= limit ; i++ ){ double t = (double)tmp ; if ( t * p[dept] > ans ) break ; tmp *= p[dept] ; if ( n % ( num * ( i + 1 ) ) == 0 ) dfs( dept + 1 , i , tmp , num * ( i + 1 ) ) ; } } int main(){ while( cin >> n ){ ans = inf ; dfs( 0 , 63 , 1 , 1 ) ; cout << ans << endl ; } return 0; }
相关问题2.
给定一个数 n,求[1,n]内约数个数最多的且数值最小的数,以及其约数个数。例:URAL 1748 ,ZOJ 2562
思路:
依然是dfs,之前是搜索到的约数个数等于题目要求时停止,现在改为搜索到的最大值比n大时停止,动态维护最大约数个数与有最大约数个数的最小值。代码:
这里就只贴URAL 1748了(ZOJ 2562 改改即可)#include<iostream> using namespace std; typedef unsigned long long lint; int p[] = { 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 , 23 , 29 , 31 , 37 , 41 , 43 , 47 , 53 } ; const lint inf = 1e18 + 5 ; lint ans , n ; int cnt ; void dfs( int dept , int limit , lint tmp , int num ) { if ( tmp > n ) return ; if ( num > cnt ) { cnt = num ; ans = tmp ; } if ( num == cnt && ans > tmp ) ans = tmp ; for ( int i = 1 ; i <= limit ; i++ ) { if ( n < (double)tmp * p[dept] ) break ; tmp *= p[dept] ; dfs( dept + 1 , i , tmp , num * (i+1) ) ; } } int main() { int t ; cin >> t ; while ( t-- ) { cin >> n ; ans = inf , cnt = 0 ; dfs( 0 , 63 , 1 , 1 ) ; cout << ans << ' ' << cnt << endl ; } return 0; }
相关问题3.
给定两个数 L,R,求[L,R]内约数个数最多的且数值最小的数,以及其约数个数。例:HDU 2521
思路:
这题其实是个水题。。因为数据范围实在太小了,暴力即可,但找不到相关例题就用这个试了一下自己的算法。我的想法是根据问题2里的算法求出[1,R]的约数个数最多的且数值最小的数x,然后判断这个数在不在[L,R]里面,如果不在的话再对[L,R]进行暴力。因为 x 很靠近 R,如果不在区间内说明L与R很靠近,可以直接暴力。
这个算法写出来的程序即使是长度超过10^9的区间也很快。
代码:
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<string> #include<map> #include<set> #include<vector> #include<queue> #include<stack> #include<bitset> #include<ctime> using namespace std; #define clr( x , y ) memset(x,y,sizeof(x)) #define cls( x ) memset(x,0,sizeof(x)) #define pr( x ) cout << #x << " = " << x << endl #define pri( x ) cout << #x << " = " << x << " " #define test( t ) int t ; cin >> t ; int kase = 1 ; while( t-- ) #define out( kase ) printf( "Case %d: " , kase++ ) #define mp make_pair #define pii pair<int,int> #define fi first #define se second #define pb push_back typedef long long lint; typedef long long ll; typedef long long LL; int l , r ; int ans , best ; const int inf = 0x3f3f3f3f ; int p[] = { 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,57 } ; void dfs( int dept , int lim , lint tmp , int num ) { if ( tmp > r ) return ; if ( best < num ) { best = num ; ans = tmp ; } if ( num == best && ans > tmp ) ans = tmp ; for ( int i = 1 ; i <= lim ; i++ ) { if ( tmp * p[dept] > r ) break ; dfs( dept + 1 , i , tmp *= p[dept] , num * ( i + 1 ) ) ; } } int pf[100][2] ; int getFac( int n ) { cls( pf ) ; int k = 0 ; for ( int i = 2 ; i * i <= n ; i++ ) { if ( n % i == 0 ) { pf[k][0] = i ; while ( n % i == 0 ) { pf[k][1] ++ ; n /= i ; } k++ ; } } if ( n > 1 ) { pf[k][0] = n ; pf[k++][1] = 1 ; } return k ; } int get( int x ) { int len = getFac( x ) ; int res = 1 ; for ( int i = 0 ; i < len ; i++ ) { res *= ( pf[i][1] + 1 ) ; } return res ; } int find( int L , int R ) { int res = l , tmp = 0 ; for ( int i = L ; i <= R ; i++ ) { int num = get( i ) ; if ( num > tmp ) { tmp = num ; res = i ; } } return res ; } void work() { ans = inf , best = 0 ; dfs( 0 , 63 , 1 , 1 ) ; if ( l <= ans && ans <= r ) cout << ans << endl ; else cout << find( l , r ) << endl ; } int main() { int t ; cin >> t ; while ( t-- ) { cin >> l >> r ; work() ; } return 0; }
相关问题4.
给定一个数k,求出一个最小正整数X,满足X的约数个数为X-K。例:HDU 4542
思路:
HDU 4542的具体题意是这样的:给出一个数K和操作类型Type:
若 Type == 0,求出一个最小正整数X,满足X的约数个数为K。
若 Type == 1,求出一个最小正整数X,满足X的约数个数为X-K。
对于 Type == 0 ,用问题1的算法直接搞即可,但这题TMD只给了200ms,必须用第二个经过剪枝的算法。
对于 Type == 1 ,可以这么想:对于一个数 n ,它有 x 个约数,k 个非约数,显然 n = x + k ,n 的约数个数为 x 废话。
那么对于给定的 k ,我们只要从2开始升序枚举约数个数 x ,使 x + k 的约数个数为 x 即是答案,又因为一个数的约数最多只有 2(√x+k) 个,如果枚举到 2(√x+k) 依然无答案则输出”Illgeal” 。
当然也有一种方法来直接预处理出非约数个数对应的值,因为非约数个数 k 对应的值 x 很接近 k,所以直接两层循环搞也是可以的:
void Init(){ for ( int i = 1 ; i <= 50000 ; i++ ) findx[i] = i; for ( int i = 1 ; i <= 50000 ; i++ ) { for ( int j = i ; j <= 50000 ; j += i) findx[j]--; if ( !findx[findx[i]] ) findx[findx[i]] = i; findx[i] = 0; } }
代码:
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<string> #include<map> #include<set> #include<vector> #include<queue> #include<stack> #include<bitset> #include<ctime> using namespace std; #define clr( x , y ) memset(x,y,sizeof(x)) #define cls( x ) memset(x,0,sizeof(x)) #define pr( x ) cout << #x << " = " << x << endl #define pri( x ) cout << #x << " = " << x << " " #define test( t ) int t ; cin >> t ; int kase = 1 ; while( t-- ) #define out( kase ) printf( "Case %d: " , kase++ ) #define mp make_pair #define pii pair<int,int> #define fi first #define se second #define pb push_back typedef unsigned long long lint; typedef long long ll; typedef long long LL; int type , k ; const lint inf = ( 1LL << 63 ) ; const int N = 50000 ; lint ans ; int pf[100][2] ; bool noprime[N+5] ; vector<int>p ; int getPri() { cls( noprime ) ; int m = (int)sqrt( N + 0.5 ) ; for ( int i = 2 ; i <= m ; i++ ) { if ( !noprime[i] ) for ( int j = i * i ; j <= N ; j += i ) noprime[j] = true ; } for ( int i = 2 ; i <= N ; i++ ) if ( !noprime[i] ) p.pb(i) ; return p.size() ; } int getFac( int n ) { cls( pf ) ; int k = 0 ; for ( int i = 0 ; p[i] * p[i] <= n ; i++ ) { if ( n % p[i] == 0 ) { pf[k][0] = p[i] ; while ( n % p[i] == 0 ) { pf[k][1] ++ ; n /= p[i] ; } k++ ; } } if ( n > 1 ) { pf[k][0] = n ; pf[k++][1] = 1 ; } return k ; } void dfs( int dept , int lim , lint tmp , int num ) { if ( num > k ) return ; if ( num == k && ans > tmp ) ans = tmp ; for ( int i = 1 ; i <= lim ; i++ ) { if ( ans / p[dept] < tmp ) break ; tmp *= p[dept] ; if ( k % ( num * ( i + 1 ) ) == 0 ) dfs( dept + 1 , i , tmp , num * ( i + 1 ) ) ; } } int get( int x ) { int len = getFac( x ) ; int res = 1 ; for ( int i = 0 ; i < len ; i++ ) res *= ( pf[i][1] + 1 ) ; return res ; } void work() { if ( !type ) { ans = inf ; dfs( 0 , 62 , 1 , 1 ) ; if ( ans > ( 1LL << 62 ) ) puts( "INF" ) ; else cout << ans << endl ; } else { int tmp = 2 ; while ( tmp * tmp <= 4 * ( tmp + k ) ) { if ( tmp == get( tmp + k ) ) { cout << tmp + k << endl ; return ; } tmp ++ ; } puts( "Illegal" ) ; } } int main() { getPri() ; test(t) { scanf( "%d%d" , &type , &k ) ; out( kase ) ; work() ; } return 0; }
相关文章推荐
- 1.m分解阶乘之和
- 2.几种递推数
- 3.欧拉函数
- 4.快速幂模m算法
- 5.扩展欧几里得&&中国剩余定理
- 6.数论_web
- 编程之美2015初赛A
- 数论题集
- 阶与原根学习笔记
- HDU 1299 Diophantus of Alexandria
- Leftmost Digit(HDU 1060)
- Rightmost Digit(HDU 1061)
- ZOJ 2674 Strange Limit 欧拉定理
- LeetCode-Palindrome Number
- 组合数求模总结
- 【数论】组合数求模
- UVALive 6396 Factors 反素数!!
- [BZOJ1041][HAOI2008][数学乱搞]圆上的整点
- HDU 5341 Gcd and Lcm
- 【数论学习】奇素数分解为两个数平方和