题目链接:
题目大意:
给出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 );
}
}