51nod-1616 最小集合(數論)
阿新 • • 發佈:2018-12-24
基準時間限制:1 秒 空間限制:131072 KB 分值: 80 難度:5級演算法題
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
C++的執行時限為:1000 ms ,空間限制為:131072 KB 示例及語言說明請按這裡
題解:該集合中一定存在輸入的數字中若干數的最大公因數。
這個證明比較簡單,例如我們有 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一定存在於該集合。
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; #define maxn 100005 int n,a[maxn],b[maxn*10],sum[maxn*10],ans; int gcd(int a,int b) { if(a%b==0) return b; return gcd(b,a%b); } int main(void) { int mx=0; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) mx=max(mx,a[i]),b[a[i]]=1; for(int i=1;i<=mx;i++) for(int j=i;j<=mx;j+=i) if(b[j]) sum[i]++; for(int i=1;i<=mx;i++) { if(sum[i]==0) continue; int flag=0; for(int j=i+i;j<=mx;j+=i) if(sum[i]==sum[j]) { flag=1; break; } if(!flag) ans++; } printf("%d\n",ans); return 0; }