http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1616
该题目的就是找到集合内的数的所有最大公约数。
题解:
1. 首先容易证明:x是集合内的数一定是当且仅当,x为初始给定的集合内的n个数中的某几个数的最大公约数。
2.
若令f(x)代表集合内能被x整除的数的个数。
那么若某个数y,集合内有f(y)个数被y整除,这些数的集合设为S,那么S内的最大公约数设为k。
那么f(k)>=|S|=f(y)。
又有y|k,所以f(k)<=f(y)。
从而有f(y)=f(k)。
因此对于y的集合S,一定有一个公约数k,f(k)=f(y), y|k。
所以如果对于y*2, y*3...,其f值都不跟f(y)相等,那么找不到k,k就只能是y, y就是最大公约数。
如果存在某个f(y'=y*t)值跟f(y)相等。那么y'对应的集合S'刚好就是y对应的集合S。
也就是说集合S的最大公约数一定是y的倍数,因此不可能再找到一个集合其公约数是y,(因为集合S已经是最大的可能集合了,被排除)。
因此y不可能是要找的最大公约数。
综上所述,题目转化为:
1.先求f值,
2. 然后只要对y,找其2*y,3*y...,是否存在某个f值与f(y)相等,来判断y是否是所求的数
算法的两部分,均能用Eraosthenes筛素数法的类似流程来实现,复杂度均为nlogn,n为数的最大范围。
不知道还有更好的方法么,如果有,那基本就是O(n)的方法,没想到。
AC代码:
#include<iostream>
using namespace std;
#define mylen 1000001
int n, myresult;
int f[mylen];
void getInput()
{
scanf("%d", &n);
int tmp;
for (int i = 0; i < n; ++i)
{
scanf("%d", &tmp);
f[tmp] = 1;
}
}
void eraosthenes_Sieve(int n)
{
for (int i = 1; i <= n; ++i)
{
for (int j = (i<<1); j <= n; j+=i)
{
if (f[j])
{
++f[i];
}
}
}
bool is_target = true;
for (int i = 1; i <= n; ++i)
{
if (f[i])
{
is_target = true;
for (int j = (i<<1); j <= n; j += i)
{
if (f[j] == f[i])
{
is_target = false;
break;
}
}
if (is_target)
{
++myresult;
}
}
}
}
int main()
{
getInput();
eraosthenes_Sieve(1000001);
printf("%d\n", myresult);
return 0;
}