首页 > 代码库 > [容斥原理] hdu 5072 Coprime

[容斥原理] hdu 5072 Coprime

题意:

给n个数,求这n个数取3个数构成一个集合, 这三个数两两互质或者两两不互质,为有多少种取法。

思路:

首先这可以转换成一个单色三角形的问题。

就是三个数为三角形的三个顶点,两个顶点如果互质则边为1,不互质边为0

为三条边均为1或者均为0的三角形就多少个。

因为三角形的总数我们是知道的,然后我们取对立面算。

那么其实我们可以枚举顶点,然后求出每个数在这些数中和它互质的有多少个,不互质的有多少个

然后各取一个就是我们要的三角形了。

我们假设互质的有X个,不互质的有Y个

那么以Z为顶点的三角形就有(X*Y)/2

最后三角形的总数就是C(N,3)

相减就是答案了

代码:

#include"cstdlib"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"queue"
#include"algorithm"
#include"iostream"
#define inf 99999999
#define N 100000
#define ll __int64
using namespace std;
int yz[N+20],v[N+20];
int mark[500],mcnt,cc[500];
__int64 n;
ll dfs(int kk,int x,int lit)
{
    if(x==lit)
    {
        ll tep=1;
        for(int i=0; i<lit; i++) tep*=cc[i];
        return yz[tep];
    }
    ll ans=0;
    for(int i=kk+1; i<mcnt; i++)
    {
        cc[x]=mark[i];
        ans+=dfs(i,x+1,lit);
    }
    return ans;
}
ll solve()
{
    ll ans=n*(n-1)*(n-2)/6;
    ll fuck=0;
    for(int i=0; i<n; i++)
    {
        ll sum=0;
        int tep=v[i];
        mcnt=0;
        memset(mark,0,sizeof(mark));
        for(int j=2; j*j<=tep; j++)
        {
            if(tep%j==0) mark[mcnt++]=j;
            while(tep%j==0) tep/=j;
        }
        if(tep>1) mark[mcnt++]=tep;
        for(int i=1; i<=mcnt; i++)
        {
            if(i%2) sum+=dfs(-1,0,i);
            else sum-=dfs(-1,0,i);
        }
        if(sum!=0) fuck+=(sum-1)*(n-sum);
    }
    return ans-fuck/2;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        scanf("%I64d",&n);
        memset(yz,0,sizeof(yz));
        for(int i=0; i<n; i++)
        {
            scanf("%d",&v[i]);
            for(int j=1; j*j<=v[i]; j++)
            {
                if(v[i]%j==0)
                {
                    yz[j]++;
                    if(v[i]/j!=j) yz[v[i]/j]++;
                }
            }
        }
        printf("%I64d\n",solve());
    }
    return 0;
}

[容斥原理] hdu 5072 Coprime