ACM里的反素数问题

定义

对于正整数 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=pa11pa22pass
那么 n 的约数个数 g(x)=(a1+1)(a2+1)(as+1)

例如: 12=223

图片来自 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 1748ZOJ 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值