1. 程式人生 > >51nod-1616 最小集合(數論)

51nod-1616 最小集合(數論)

基準時間限制: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;
}