51nod 最小集合

System Message (命題人)
基準時間限制:1 秒 空間限制:131072 KB 分值: 80

A君有一個集合。

這個集合有個神奇的性質。

若X,Y屬於該集合,那麼X與Y的最大公因數也屬於該集合。

但是他忘了這個集合中原先有哪些數字。

不過幸運的是,他記起了其中n個數字。

當然,或許會因爲過度緊張,他記起來的數字可能會重複。

他想還原原先的集合。

他知道這是不可能的……

現在他想知道的是,原先這個集合中至少存在多少數。


樣例解釋:

該集合中一定存在的是{1,2,3,4,6}


Input
第一行一個數n(1<=n<=100000)。
第二行n個數,ai(1<=ai<=1000000,1<=i<=n)。表示A君記起來的數字。
輸入的數字可能重複。
Output
輸出一行表示至少存在多少種不同的數字。
Input示例
5
1 3 4 6 6
Output示例
5

分析: 只想到和倍數有關,之後的就沒想出來,還是老老實實看題解吧
官方題解:
觀察題目性質。
性質1:該集合中一定存在輸入的數字中若干數的最大公因數。
這個證明比較簡單,例如我們有 a1, a2, ..., an 這些數,那麼 gcd(a1,a2) 一定存在該集合,然後 gcd(a1,a2,a3) 也一定存在該集合,依次類推。
所以我們對於每個數i,都求出在n個數中有多少數是它的倍數,記爲 f(i) 。
然後觀察 f(2× i), f(3× i), .., f(x× i), ... 中是否存在一個數等於 f(i) ,若不存在,則i一定存在於該集合。
總複雜度爲 maxni=1 ai× lg(maxni=1 ai) 。

#include <stdio.h>

const int N = 1000100;
bool vis[N];
int cnt[N];

int main()
{
    int n, x, limit = 0;
    scanf("%d", &n);
    
    for(int i = 0; i < n; i++)
    {
        scanf("%d", &x);
        vis[x] = true;
        limit = limit < x ? x : limit;
    }
    
    for(int i = 1; i <= limit; i++)
        for(int j = i; j <= limit; j += i)
        {
            if(vis[j]) cnt[i]++;
        }
    
    int ans = 0;
    for(int i = 1; i <= limit; i++)
    {
        bool ok = true;
        if(cnt[i])
        {
            for(int j = i << 1; j <= limit; j += i)
            {
                if(cnt[j] == cnt[i])
                {
                    ok = false;
                    break;
                }
            }
        }
        else ok = false;
        if(ok) ans++;
    }
    
    printf("%d\n", ans);
    
    return 0;
}










發佈了31 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章