题目链接:http://poj.org/problem?id=3904
类似题目poj2773(题解):http://blog.csdn.net/DOLFAMINGO/article/details/72669432
题解:
给出n个数, 随便挑4个, 使得这四个数的最大公约数为1, 问有多少种组合?
思路:先用容斥原理计算出四个数的最大公约数>=1的组合数, 然后再用总数C(n,4)减之。
1.将每个数进行分解质因数, 然后再根据这些质因数组合出不同的因子,并记录这个因子出现的次数以及由多少个质因数构成。
2.容斥原理:比如因子2的个数为a,则四个数公约数为2的个数 为C(a,4),因子3的个数为b,则四个数公约数为3的个数为C(b,4),因子6(2*3)的个 数为c,则四个数公约数的个数为C(c,4)。 但是公约数为2的情况中或者公约数为3的情况中可能包括公约数为6的情况,相当于几个集合求并集,这就需要容斥定理来做。
3.如果这个因子出现的次数>=4, 则表明这个因子可以作为某四个数的最大公约数的因子。
4.根据容斥原理:当这个因子的由奇数个质因数构成时, 加; 当这个因子由偶数个质因子构成时, 减。
5. ans = C(n,4) - gcd(a,b,c,d)!=1的组合数。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <sstream>
#include <algorithm>
using namespace std;
#define pb push_back
#define mp make_pair
#define ms(a, b) memset((a), (b), sizeof(a))
#define eps 0.0000001
typedef long long LL;
const int INF = 2e9;
const LL LNF = 9e18;
const int mod = 1e9+7;
const int maxn = 1e4+10;
LL pri[maxn], fac_num[maxn], fac_pri[maxn];
LL n, cnt;
LL C(LL x)
{
return x*(x-1)*(x-2)*(x-3)/24;
}
void Divide(LL x)
{
cnt = 0;
for(int i = 2; i*i<=x; i++)
{
if(x%i==0)
{
pri[cnt++] = i;
while(x%i==0) x /= i;
}
}
if(x!=1) pri[cnt++] = x;
}
void Unit()
{
for(LL s = 1; s < (1<<cnt); s++) //用二进制, 亦可用递归
{
LL tmp = 1, sum = 0;
for(int j = 0; j<cnt; j++)
if(s&(1<<j))
{
tmp *= pri[j];
sum++;
}
fac_num[tmp]++;
fac_pri[tmp] = sum;
}
}
void init()
{
ms(fac_num, 0);
ms(fac_pri, 0);
LL x;
for(int i = 1; i<=n; i++)
{
scanf("%lld",&x);
Divide(x); //分解质因数
Unit(); //质因数可以组成哪些因子(这些因子就是四个数的约数)
}
}
void solve()
{
LL tmp = 0;
for(int i = 1; i<=1e4; i++) //容斥, 计算gcd(a,b,c,d)!=1的个数
{
if(fac_num[i]>=4) //这个因子的个数必须不小于4, 才能成为4个数的约束
{
if(fac_pri[i]&1) //素数个数为奇数时, 加
tmp += C(fac_num[i]);
else //素数个数为偶数时, 减
tmp -= C(fac_num[i]);
}
}
LL ans = C(n) - tmp; //总的减去gcd(a,b,c,d)!=1的个数,即为gcd(a,b,c,d)=1的个数。
printf("%lld\n", ans);
}
int main()
{
while(scanf("%lld",&n)!=EOF)
{
init();
solve();
}
}