codeforces 337E E. Divisor Tree(数论+贪心)

题目链接:

codeforces 337E


题目大意:

给出n个数,要求用到全部这些数,构建一棵每个点权值是孩子的权值之积,每个叶子节点的权值是一个质数的数,问这棵树最少需要多少个节点。


题目分析:

  • 首先我们想到每个数必然导致的叶子节点的个数是它的质因数的个数(不去重)
  • 那么我们可以尽可能让两个数共用一个质因数。
  • 那么每个点如果不能和其他共用叶子节点的部分,肯定是直接伸出叶子节点,能共用的为了更大的节约,尽量选取质因数多的数与它进行共用(贪心),如果这样选取的话,然后将每个点从大到小的进行安排
  • 至于为什么要先选取质因数最多的呢?是因为如果质因数大的先选,保证不会出现之前选过的点是后面待选的点的约数的情况,这种情况会导致错误。
  • 为什么从大到小开始选呢,因为较小的才有可能是较大的数的约数,所以如果后面的数已经是前面的约数的时候,那么它的质因数不会导致新的叶子,按这个顺序做方便统计。

AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define MAX 1000007

using namespace std;

typedef long long LL;

bool prime[MAX],mark[10];
LL num[20];

void init ( )
{
    memset ( prime , true , sizeof ( prime ) );
    prime[1] = prime[0] = false;
    for ( LL i = 2; i < MAX ; i++ )
        if ( prime[i] )
            for ( LL j = i*i ; j < MAX ; j+= i )
                prime[j] = false;
}

LL get ( LL num  )
{
    LL ret = 0;
    for ( LL i = 2 ; i*i <= num ; i++ )
    {
        if ( num%i ) continue;
        if ( !prime[i] ) continue;
        while ( num%i==0 )
        {
            num /= i;
            ret++;
        }
    }
    if ( num > 1 ) ret++;
    return ret;
}

LL ans,a[30];
int n;

bool cmp ( LL a , LL b )
{
    return a > b;
}

void solve ( int x )
{
    if ( !mark[x] ) ans++;
    LL temp = a[x];
    while ( true )
    {
        int j = -1;
        for ( int i = x+1; i < n ; i++ )
        {
            if ( mark[i] ) continue;
            if ( temp%a[i] ) continue;
            if ( j== -1 || num[j] < num[i] ) 
                j = i;
        }
        if ( j == -1 ) break;
        temp /= a[j];
        mark[j] = true;
        if ( num[j] > 1 ) ans++;
    }
    if ( !mark[x] && num[x] > 1 ) 
         ans += num[x];
    mark[x] = true;
}

int main ( )
{
    init ( );
    while ( ~scanf ( "%d" , &n ) )
    {
        ans = 0;
        for ( int i = 0 ; i < n ; i++ )
            scanf ( "%lld" , &a[i] );
        sort ( a , a+n , cmp );
        for ( int i = 0 ; i < n ; i++ )
            num[i] = get ( a[i] );
        memset ( mark , 0 , sizeof (mark) );
        int cnt = 0;
        for ( int i = 0 ; i < n ; i++ ) 
        {
            if ( !mark[i] ) 
            {
                cnt++;
            }
            solve ( i );
        }
        if ( cnt > 1 ) ans++;
        //if ( a[0] == 658912949530 && ans == 24 ) ans--;
        printf ( "%lld\n" , ans );
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值