poj 3904

题意:给出n个数,问有多少组(a,b,c,d)公约数为1。

题解:有两种思路,一个是mobius反演,一种是容斥。

先介绍一下容斥的思路:我们考虑该问题的逆问题,求多少组数的gcd>=2,那么集合gcd为质数的交集就是我们要答案咯。枚举出每个素数乘积的因子数字,然后容斥筛一遍就可以了

(2017.9.2 这里补充一下,关于为什么用包含某个素因子个数的并集可以表示所有解。有点类似数筛的思想。比如,我们计算包含2这个因子的个数的时候,是不是把包含2,4,6,8等因子的结果一并算进去了,3的时候也是一样,把,3,6,9。这个过程和埃氏筛法的思想类似,一定可以包含所有的情况,然后其并集就是我们要的答案)

#include<stdio.h>  
#include<string.h>  
using namespace std;  
#define LL long long  
#define maxn 10005  
  
LL node[maxn],num[maxn],vist[maxn],prime[maxn];  
void Init()  
{  
    LL i;  
    for(i=4;i<maxn;i++)  
        node[i]=i*(i-1)*(i-2)*(i-3)/24;  
}  
  
void make_count(int m)  
{  
    int i,j,tmp,flag,cnt=0;  
    for(i=2;i*i<=m;i++)  
        if(m&&m%i==0)  
        {  
            prime[cnt++]=i;  
            while(m&&m%i==0)  
                m/=i;  
        }     
    if(m>1)  
        prime[cnt++]=m;  
    for(i=1;i<(1<<cnt);i++)  
    {  
        tmp=1,flag=0;  
        for(j=0;j<cnt;j++)  
            if(i&(1<<j))  
                flag++,tmp*=prime[j];  
        num[tmp]++;  //统计当前因子出现的次数  
        vist[tmp]=flag; //记录当前因子是由多少个素因子组成,奇加偶减  
    }  
}  
  
int main()  
{  
    Init();  
    int n,i,x;  
    while(~scanf("%d",&n))  
    {  
        memset(num,0,sizeof(num));  
        memset(vist,0,sizeof(vist));  
        for(i=0;i<n;i++)  
        {  
            scanf("%d",&x);  
            make_count(x);  
        }  
        LL ans=0;  
        for(i=1;i<maxn;i++)  
            if(num[i])  
            {  
                if(vist[i]&1)  
                    ans+=node[num[i]];  
                else  
                    ans-=node[num[i]];  
            }  
        printf("%I64d\n",node[n]-ans);            
    }  
    return 0;  
}  
容斥

mobius:常见的为求gcd()==k 或者gcd为质数的情况

我们令
f(x)=四个数的gcd是x的情况数 ;

F(x)=四个数的gcd是x的倍数的情况;

这里要求的n为1。

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
typedef long long ll;
ll num[10001];
ll mu[10001];
ll c[10001];
ll vis[10001];
ll prime[10001];
void init()
{
    for(ll i=1;i<=10000;i++)
    {
        c[i]=i*(i-1)*(i-2)*(i-3)/24;
    }
    memset(mu,0,sizeof(mu));
    memset(vis,0,sizeof(vis));
    memset(prime,0,sizeof(prime));
    int ret=0;
    mu[1]=1;
    for(int i=2;i<=10000;i++)
    {
        if(!vis[i])
        {
            mu[i]=-1;
            prime[ret++]=i;
        }
        for(int j=0;j<ret &&i*prime[j]<=10000;j++)
        {
            ll temp=i*prime[j];
            vis[temp]=1;
            if(i%prime[j])
            {
                mu[temp]=-mu[i];
            }
            else break;
        }
    }
}
void get(ll x)
{
    for(ll i=1;i*i<=x;i++)
    {
        if(x%i==0)
        {
            num[i]++;
            if(i*i!=x) num[x/i]++;// attention//
        }
    }
}
int main()
{
    int n;
    init();//
    while(~scanf("%d",&n))
    {
        ll x;
        memset(num,0,sizeof(num));
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&x);
            get(x);
        }
        ll ans=0;
        for(int i=1;i<=10000;i++)
        {
            if(mu[i]==0 || num[i]<4) continue;
            ans+=mu[i]*c[num[i]];
        }
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2017-08-09 15:11  猪突猛进!!!  阅读(177)  评论(0编辑  收藏  举报