按照惯例,先附上题目链接http://poj.org/problem?id=3904
这个题大体意思为给你一组数,让你4个一组,看有多少种组合,满足它们的公因子为1.
先来说下这个题目有什么难点,一是组合数目的问题N≤1e5, 还好最多知道C(1e5, 4), long long int 应该够了二是查找哪些组合满足条件,假设我们遵奉"万物皆可暴力"大法,不停地枚举,直接C(n, 4),测评姬估计要TLE了。三是四个数的公因子为1,这个还好,看成是gcd的拓展版就可。
所以此题关键就在于找出哪些组合满足条件~~。
四个数的公约数为1,并不代表四个数两两互质。比如(2,3,4,5)公约数为1,但是2和4并不互质。从反面考虑,先求出四个数公约数不为1的情况个数,用总的方案个数减去四个数公约数不为1的情况个数就是所求。求四个数公约数不为1的情况个数,需要将N个数每个数质因数分解,纪录下所有不同的素因子所能组成的因子(就是4个数的公约数),并统计构成每种因子的素因子个数,和因子总数。然后再计算组合数。比如说因子2的个数为a,则四个数公约数为2的个数为C(a,4),因子3的个数为b,则四个数公约数为3的个数为C(b,4),因子6(2*3)的个数为c,则四个数公约数的个数为C(c,4)。但是公约数为2的情况中或者公约数为3的情况中可能包括公约数为6的情况,相当于几个集合求并集,这就需要容斥定理来做。容斥原理应用,以2为因子的数有a个,3为因子 的数有b个,6为因子的数有c个,n个数不互质的四元组个数为C(a,4)+C(b,4)-C(c,4) (含奇数个素因子的加,偶数个素因子的减下面就是统计出2,3,5这些因子的倍数的个数,对C(a,4)容斥!
AC代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 10005;
ll prime[maxn], p[maxn], num[maxn], count[maxn];
void init()
{
memset(p, 0, sizeof(p));
memset(num, 0, sizeof(num));
for(ll i = 4; i < maxn; i++){
p[i] = i*(i-1)*(i-2)*(i-3)/24;
}
}
void solve(int n)
{
int cnt = 0;
for(int i = 2; i*i <= n; i++){
if(n % i == 0){
prime[cnt++] = i;
n /= i;
}
while(n % i == 0){
n /= i;
}
}
if(n != 1) prime[cnt++] = n;
for(int i = 1; i < (1 << cnt); i++){ // 以二进制形式来枚举
int k = 1, sum = 0;
for(int j = 0; j < cnt; j++){
if(i & (1<<j)){
k *= prime[j];
sum++;
}
}
count[k]++;
num[k] = sum;
}
}
int main()
{
init();
int n, m;
while(~scanf("%d", &n)){
memset(count, 0, sizeof(count));
for(int i = 0; i < n; i++){
scanf("%d", &m);
solve(m);
}
ll ans = 0;
for(int i = 0; i < maxn; i++){
if(count[i]){
if(num[i]&1)
ans += p[count[i]];
else ans -= p[count[i]];
}
}
cout << p[n] - ans << endl;
}
return 0;
}
容斥原理关键在思维啊