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_());
    }
}



你可能感兴趣的:(数论,莫比乌斯反演)