测试地址:Coprime
题目大意:有
做法:这一题需要利用补集转化思想进行分析,并使用容斥原理和状态压缩进行统计。
分析这一题的模型,我们可以把每个人看做一个点,每两个点之间有连边,如果两个点的身份值互质连红边,如果两个点的身份值不互质连黑边,那么问题就转化成求这样一个图中的同色三角形的数目。考虑到直接求比较麻烦,我们就利用补集转化思想,用总数减去异色三角形数目来得到答案。总数显然就是
画个图我们就会发现,一个异色三角形对应两对有公共顶点的异色边,那么我们可以求出有公共顶点的异色边对数,用这个对数除以
注意,由于有
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
ll T,n,a[100010],fac[100010][7],mul[100010];
ll e[100010];
void find_factor(ll i)
{
ll s=i;
fac[i][0]=0;
for(int j=2;j*j<=s;j++)
if (!(s%j))
{
fac[i][++fac[i][0]]=j;
while(!(s%j)) s/=j;
}
if (s>1) fac[i][++fac[i][0]]=s;
}
int main()
{
scanf("%lld",&T);
for(int i=1;i<=100000;i++)
find_factor(i);
while(T--)
{
memset(mul,0,sizeof(mul));
memset(e,0,sizeof(e));
scanf("%lld",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
ll limit=(1<<fac[a[i]][0]);
for(int j=1;j<limit;j++)
{
ll s=j,k=1,sum=1;
while(s)
{
if (s&1) sum*=fac[a[i]][k];
s>>=1;k++;
}
mul[sum]++;
}
}
for(int i=1;i<=n;i++)
{
ll limit=(1<<fac[a[i]][0]);
for(ll j=1;j<limit;j++)
{
ll s=j,k=1,sum=1,sign=-1;
while(s)
{
if (s&1) sum*=fac[a[i]][k],sign*=-1;
s>>=1;k++;
}
e[i]+=sign*mul[sum];
}
e[i]=n-e[i];
if (a[i]==1) e[i]--;
}
ll ans=0;
for(int i=1;i<=n;i++) ans+=e[i]*(n-1-e[i]);
printf("%lld
",n*(n-1)*(n-2)/6-ans/2);
}
return 0;
}