51nod 1616 最小集合

来源:互联网 发布:收款收据打印软件 编辑:程序博客网 时间:2024/06/05 09:17

51 nod 1616 最小集合

原题链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1616

题目的意思更一般的可以理解为:

任取一些数字。它们可能出现的最大公约数。包括原始给定的数字,一共可以出现多少不同的数字。

因为:
d=gcd(x,y)t=gcd(d,c)t=gcd(x,y,c)

记:确定出现的数字中。n的倍数的个数为C[n]

那么:
gcd(i,j)=d
(i,j)对数的个数为,其中,i,j均为已确定数字集合中的数字。:
f(d)=[gcd(i,j)=d]F(d)=d|af(a)=d|id|j1=C[d](C[d]1)f(d)=d|aμ(ad)F(d)

我们从大到小计算可能出现的数字。因为没有出现的数字只可能从更大的数字产生。所以不回漏掉。

注意。如果产生了新的数字。记得更新C数组。(我就是忘记更新。debug了很久)

#include <algorithm>#include <string.h>#include <stdio.h>#include <vector>#define MAXN 1000005using namespace std;typedef long long LL;LL C[MAXN];int cnt[MAXN];int mu[MAXN]={0,-1};int main (){    for(int i=1;i<MAXN;i++)    {        mu[i]=-mu[i];        for(int j=i<<1;j<MAXN;j+=i)mu[j]+=mu[i];    }    int n,a;    scanf("%d",&n);    for(int i=0;i<n;i++)    scanf("%d",&a),cnt[a]=1;    int ans=0;    for(int i=MAXN-1;i;i--)    {        LL u=0;        for(int j=i;j<MAXN;j+=i)C[i]+=cnt[j];        if(!C[i])continue;        for(int j=i,k=1;j<MAXN;j+=i,k++)u+=(LL)mu[k]*(((C[j]-1)*C[j])>>1ll);        if(u>0||cnt[i])        {            ans++;            if(!cnt[i])C[i]++;            cnt[i]=1;        }    }    printf("%d\n",ans);    return 0;}
原创粉丝点击