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

本文解析了Codeforces337E题目的求解思路,通过分析每个数的质因数数量来构建一棵特定的树形结构,旨在使树的节点数最少。文章详细介绍了如何使用贪心算法来优化节点分配过程。

题目链接:

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 );
    }
}
Codeforces Round 1036 是一场同时面向 Div.1 和 Div.2 参赛者的比赛,通常这类比赛会包含多个具有挑战性的编程题目,涵盖算法、数据结构、数学等多个领域。比赛的题解和题目信息可以帮助参赛者回顾解题思路,提升编程能力。 ### 比赛基本信息 - **比赛名称**:Codeforces Round #1036 (Div. 1 and Div. 2) - **比赛时间**:具体时间为 UTC+X(根据实际举办日期和时间表) - **比赛链接**:[Codeforces 官方页面](https://codeforces.com/contest/1343) - **题解发布位置**:通常在比赛结束后不久,官方或社区成员会在 Codeforces 博客、GitHub 或其他技术平台上发布题解。 ### 题目类型与难度分布 该轮比赛通常包括 5 到 7 道题目,难度从简单实现到复杂算法不等。例如: - **A题**:通常是简单的模拟或数学问题。 - **B题**:可能涉及字符串处理或基础贪心策略。 - **C题**:中等难度,可能需要掌握基本的数据结构如数组、排序等。 - **D题及以后**:较高难度,可能涉及图论、动态规划、数论等高级算法。 ### 参赛情况与亮点 - **参与人数**:通常超过 10,000 名选手参加。 - **热门话题**:比赛中某些题目可能会引发广泛讨论,尤其是那些需要用到巧妙构造或优化技巧的问题。 - **知名选手表现**:顶尖选手如 tourist、Um_nik 等通常会以极快的速度完成所有题目,并占据排行榜前列。 ### 示例代码片段 以下是一个典型的 Codeforces 题目解法示例,适用于某道中等难度题目: ```cpp #include <bits/stdc++.h> using namespace std; int main() { int t; cin >> t; while(t--) { long long l, r; cin >> l >> r; // 假设 e 是一个预处理好的符合条件的数组 // 使用二分查找来统计区间 [l, r] 内的有效数字个数 long long ans = upper_bound(e.begin(), e.end(), r) - lower_bound(e.begin(), e.end(), l); cout << ans << endl; } return 0; } ``` ### 题解资源推荐 - **Codeforces 官方博客**:通常会有详细的题解和作者说明。 - **GitHub 仓库**:许多参赛者会将自己的解法上传至 GitHub,便于他人学习。 - **知乎专栏 / 优快云 / 博客园**:中文社区中也常有高质量的赛后总结与分析文章。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值