题意:
给出几个数字,求选出的数全部互质或全不互质的种数
解题思路:
同色三角形原理,一个平面上有n个点,两个点之间可能是红线,也可能是连黑线,问一共能连出多少个同色三角形?
从一个点出发,有n-1条线,假设红线有的d[ i ]条,黑线有n-1-d[ i ]条,那么从这一点出发不同色的三角形个数为d[ i ]*(n-1-d[ i ])个,所以总的不同色三角形个数为sum = d[ i ]*(n-1-d[ i ]) (i=1,2,3...n),所以同色三角形的个数是c(n,3)-sum/2(因为每条边用了两次,也就是说一个三角形被计算了两次,除以二)
这题类似的,将两个互质的数看作黑边,不互质的数看作红边,计算方法同上
那么主要要解决的就是判断两个数是否互质,这一块用容斥解决
先将100000以内的数打表,保存到prime[ ]这个数组中去
分别求出n个数的质因子,奇数个相乘的用+,偶数个用- <=== 这个地方用了容斥的方法,不懂可以调试一下
#include <iostream>
#include <cstdio>
#include <string.h>
#include <string>
using namespace std;
#define LL __int64
const int maxn = 100010;
int test;
LL n;
int a[maxn],num[maxn],k;
int prime[maxn];
bool flag[maxn];
int fact[maxn][20];
int coun[maxn];
void getprime()
{
memset(flag,0,sizeof(flag));
flag[1] = true;
prime[0]=0;
k=0;
for(int i=2;i<maxn;i++)
{
if(flag[i]==0) prime[++k] = i;
for(int j=i;j<maxn;j+=i)
{
flag[j]=1;
}
}
}
void init()
{
for(int i=2;i<=100000;i++)
{
for(int j=i+i;j<=100000;j+=i)
{
num[i]+=num[j];
}
}
}
void getFact(int dig,int pos)
{
int tmp = dig;
for(int i=1;i<=k && i*i<=tmp;i++)
{
if(tmp%prime[i]==0)
{
fact[pos][coun[pos]++]=prime[i];
while(tmp%prime[i]==0)
{
tmp/=prime[i];
}
}
if(tmp==1) break;
}
if(tmp!=1) fact[pos][coun[pos]++]=tmp;
}
int main()
{
getprime();
int t;
scanf("%d",&t);
while(t--)
{
memset(num,0,sizeof(num));
scanf("%I64d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
num[a[i]]++;
}
init();
memset(coun,0,sizeof(coun));
LL ans=0;
for(int i=1;i<=n;i++)
{
LL res=0;
getFact(a[i],i);
for(int j=1;j<(1<<coun[i]);j++)
{
LL ansNum = 1;
int cnt = 0;
for(int k=0;k<coun[i];k++)
{
if(j&(1<<k))
{
ansNum *= fact[i][k];
cnt++;
}
}
if(cnt&1) res+=(num[ansNum]-1);
else res-=(num[ansNum]-1);
}
ans+=(n-1-res)*res;
}
ans = n*(n-1)*(n-2)/6-ans/2;//n,ans要用LL
printf("%I64d\n",ans);
}
return 0;
}