题目链接
poj-3904
思路
莫比乌斯反演很好的一道入门题。这里运用莫比乌斯倍数反演.
设f(n)为gcd(a,b,c,d)=n的个数
则F(n)为gcd(a,b,c,d)=n及n的倍数的个数
则有
然后用莫比乌斯倍数反演得到
然后f(1)就是我们要求的答案,显而易见
F(n)的求法就是求数列中有几个数的因子带有n,然后用C(4,n)来求出即可;
用欧拉筛求出mu,就可以一直用,然后每次O(nsqrt(n))求出F,O(1)调用,O(n)求出答案。
复杂度应该是O(nsqrt(n))
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
| #include<iostream> #include<cstdio> #include<cmath> #include<cstring> using namespace std; #define ll long long const int maxn = 1e5+10; int mu[maxn]; bool vis[maxn]; int prime[maxn]; int a[maxn]; int tot[maxn]; int cnt,n; void get_mu() { mu[1]=1; for(int i=2;i<maxn;i++) { if(!vis[i]) { prime[cnt++]=i; mu[i]=-1; } for(int j=0;j<cnt&&i*prime[j]<maxn;j++) { vis[i*prime[j]]=true; if(i%prime[j]) { mu[i*prime[j]]=-mu[i]; } else { mu[i*prime[j]]=0; break; } } } } void get_F() { memset(tot,0,sizeof(tot)); for(int i=0;i<n;i++) { int num = sqrt(a[i]); for(int j=1;j<=num;j++) { if(a[i]%j==0) { tot[j]++; tot[a[i]/j]++; if(j*j==a[i])tot[j]--; } } } } long long C(int m) { if(m==0)return 0; return 1ll*m*(m-1)*(m-2)*(m-3)/24; } int main() { get_mu(); while(scanf("%d",&n)!=EOF) { int maxx = 0; for(int i=0;i<n;i++)scanf("%d",&a[i]),maxx=max(a[i],maxx); get_F(); ll ans = 0; for(int i=1;i<=maxx;i++){ ans+=1ll*mu[i]*C(tot[i]); } printf("%lld\n",ans); } }
|
你最愿意做的哪件事,才是你的天赋所在