poj 3904(莫比乌斯反演)

时间:2023-03-10 00:08:23
poj 3904(莫比乌斯反演)

POJ 3904

题意:

从n个数中选择4个数使他们的GCD = 1,求总共有多少种方法

Sample Input

4
2 3 4 5
4
2 4 6 8
7
2 3 4 5 7 6 8

Sample Output

1
0
34

思路:先求出选择四个数所有的情况,C(4,n) = n * (n-1) * (n-2) * (n-3),然后减去GCD为2,GCD为3......;在这过程中我们会把GCD = 6减去两次,所以需要加上。刚好满足莫比乌斯函数

函数:合数为0 ,质数数目为奇  -1,质数数目为偶 1

先筛出mu函数,然后求即可

Tc_To_Top非常感谢

/*
POJ3904
Tc_To_Top:http://blog.csdn.net/tc_to_top/article/details/49130111
非常感谢,让我对莫比乌斯有了进一步了解- -/*毕竟弱
以前对这个求GCD一直很模糊,
C(n,k) - C(gcd只含奇数个质数的个数,k) + C(gcd只含偶数个质数的个数,k),前面的符号就是莫比乌斯函数
先求出所有可能的情况,然后容斥原理需要减去以及加上一些数,而这就极好的利用了莫比乌斯原理。
不是有一种求1≤x≤a,1≤y≤b中一共有多少对互质的数
for(int i = 1;i <= n;i++)
ans += mu[i]*(n/i)*(n/i);
感觉就是上面原理的压缩版,i等于一时求出所有情况,然后减去GCD=2,....加上GCD=6...
(#‵′)靠,感觉自己好坑 - -! 居然纠结半天 */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#include <algorithm>
#include <functional>
typedef long long ll;
using namespace std; const int inf = 0x3f3f3f3f;
const int maxn = 1e5;
int tot;
int is_prime[maxn];
int mu[maxn];
int prime[maxn]; void Moblus()
{
tot = 0;
mu[1] = 1;
for(int i = 2; i < maxn; i++)
{
if(!is_prime[i])
{
prime[tot++] = i;
mu[i] = -1;
} for(int j = 0; j < tot && i*prime[j] < maxn; j++)
{
is_prime[i*prime[j]] = 1;
if(i % prime[j])
{
mu[i*prime[j]] = -mu[i];
}
else
{
mu[i*prime[j]] = 0;
break;
}
}
}
} int tmax;
int num[maxn],cnt[maxn];
ll get_()
{
for(int i = 1; i <= tmax; i++)
{
for(int j = i; j <= tmax; j+=i)
{
cnt[i] += num[j]; //计算GCD为i的集合中的个数
}
}
ll ans = 0;
for(int i = 1; i <= tmax; i++)
{
int tt = cnt[i];
if(tt >= 4)
ans += (ll)mu[i]*tt*(tt-1)*(tt-2)*(tt-3)/24;
}
return ans;
} int main()
{
int n;
Moblus();
while(scanf("%d",&n)!=EOF)
{
memset(num,0,sizeof(num));
memset(cnt,0,sizeof(cnt));
for(int i = 0; i < n; i++)
{
int tt;
scanf("%d",&tt);
num[tt] ++;
tmax = max(tmax,tt);
}
if(n < 4)
printf("0\n");
else
printf("%lld\n",get_());
}
}