题意:
给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;
}
原文:http://blog.csdn.net/wdcjdtc/article/details/40898175