题意简述:给定一个序列,求其所能产生的gcd个数。
分析:正向枚举时间复杂度为O(2^n),直接T飞。但观察到i其数值范围仅为1e6,故考虑反向枚举每一个数能否被生成,对于一个数a,显然只能被其倍数的gcd生成,关键是如何判断序列中a的倍数能否生成a。设a的两个倍数为pa、qa,显然gcd(pa,qa)=gcd(p,q)*a>=a,且仍然为a的倍数,因此只要判断能产生的最小gcd和a是否相等即可,因为参与gcd的数越多,该值越小,所以只需判断全部a的倍数的gcd值是否等于a即可。时间复杂度为O(MlogM),其中M为值域。
代码:
#include<stdio.h>
#include<vector>
using namespace std;
const int N=1e6+10;
int n,val;
bool vis[N];
int gcd(int a,int b){return b?gcd(b,a%b):a;}
vector<int> num;
bool judge(int a)
{
num.clear();
for (int i=1;a*i<=val;i++)
if (vis[a*i]) num.push_back(i);
if (num.size()<2) return false;
int tmp=gcd(num[0],num[1]);
for (int i=2;i<num.size();i++)
tmp=gcd(tmp,num[i]);
return tmp==1;
}
signed main()
{
scanf("%d",&n);
val=-1;
for (int i=1;i<=n;i++)
{
int tmp; scanf("%d",&tmp);
vis[tmp]=true;
if (tmp>val) val=tmp;
}
int ans=0;
for (int i=1;i<=val;i++)
if ((!vis[i])&&(judge(i))) ans++;
printf("%d",ans);
return 0;
}