最小公倍数 - 唯一分解定理

题意:给一个数字n,范围在[1,2^23-1],这个n是一系列数字的最小公倍数,这一系列数字的个数至少为2 个

例如12,是1和12的最小公倍数,是3和4的最小公倍数,是1,2,3,4,6,12的最小公倍数,是12和12的最小公倍数…

那么找出一个序列,使他们的和最小,上面的例子中,他们的和分别为13,7,28,24……显然最小和为7

 

思路分析 :可以确认的一点就是所要找的数之间两两互质

  反证法 : 现有两个不是互质的数,他们的最小公倍数为 n , gcd 不为 1 ,此时计算可以得到一个和, 如果让这两个数同时除以 gcd , 那么我们再次计算和的时候会发现和是明显减少的

 

  如果这点明确后,就可以根据唯一分解定理去计算答案了,因为其可以保证所有相乘的数之间两两互质

 注意要特判一些情况

代码示例 :

#define ll long long
const int maxn = 1e6+5;
const int mod = 1e9+7;
const double eps = 1e-9;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;

ll n;
vector<ll>ve;
ll pt[maxn];
ll cnt[maxn];

void init(){
    memset(pt, 1, sizeof(pt)); 
    for(ll i = 2; i <= 1000000; i++){
        if (pt[i]){
            ve.push_back(i);
            for(ll j = i+i; j <= 1000000; j += i) pt[j] = 0;
        }
    }    
}

int main() {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    init();
    int kase = 1;
    
    while(~scanf("%lld", &n) && n){
        memset(cnt, 0, sizeof(cnt));   
        if (n == 1) {printf("Case %d: 2\n", kase++); continue;} 
        for(ll i = 0; i < ve.size(); i++){
            while(n%ve[i] == 0){
                n /= ve[i];
                cnt[i]++;
            }
            if (n == 1) break;
        }
        ll ans = 0;
        int f = 0;
        if (n != 1) {ans = n; f++;}
        for(ll i = 0; i < ve.size(); i++){
            if (cnt[i]) {
                ans += pow(ve[i], cnt[i]);
                f++;
            }
        }
        if (f == 1) ans++;
        printf("Case %d: %lld\n",kase++, ans);
    }
    return 0;
}

 

posted @ 2018-04-19 20:22  楼主好菜啊  阅读(624)  评论(0编辑  收藏  举报