HDU 5072
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5072
题意:
给n个数,问有多少对数(a,b,c)使得gcd(a,b)=gcd(b,c)=gcd(a,c)=1,或者gcd(a,b),gcd(b,c),gcd(a,c)均不为1。
思路:
单求为1的倒还好办,因为数字都在1e5以内应该可以用一种质因数分解的方式求出来,但是第二个条件就不好求了。
所以问题转化一下,求tans = 不合法的对数,然后ans = 总数 - tans。
举了几个例子发现,对于每个数求出与他互质的数的个数u,剩下的就是不与他互质的数(n-1-u),则tans = sigma(u*(n-1-u)) / 2。证明的话大概用容斥原理。
然而怎么求与他互质的数的个数……然后就卡死了。
从网上搜解题报告,还是利用容斥原理来求。
对于一个数,质因数分解得到它的质因数数组ele[],然后每个ele最多使用一次能构成的倍数,给这些数的计数器+1,表示此数不与这些数互质。
在求与一个数互质的数dp时,进行同样的质因数分解得到质因数数组ele[],每个ele最多使用一次构成倍数temp。如果ele使用了奇数个,dp+=num[temp](num是计数器,temp表示这个倍数),否则dp-=num[temp]。具体可以用全概率公式的原理来解释。
源码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
#define LL long long
const int MAXN = 200000 + 5;
int vis[MAXN];
int g[MAXN];
int num[MAXN];
int p[MAXN], cnt;
int data[MAXN];
void init()
{
memset(vis, 0, sizeof(vis));
cnt = 0;
for(int i = 2 ; i < MAXN ; i++){
if(vis[i] == 0){
p[cnt++] = i;
int u = i;
while(u < MAXN) vis[u] = 1, u += i;
}
}
}
int ele[MAXN];
int main()
{
// freopen("C.in", "r", stdin);
init();
int t;
scanf("%d", &t);
while(t--){
int n;
scanf("%d", &n);
memset(g, 0, sizeof(g));
for(int i = 0 ; i < n ; i++)
scanf("%d", &data[i]), g[data[i]] = 1;
memset(num, 0, sizeof(num));
for(int i = 0 ; i < n ; i++){
cnt = 0;
int temp = data[i];
for(int j = 0 ; p[j] * p[j] <= temp ; j++){
if(temp % p[j] == 0){
ele[cnt++] = p[j];
// printf("cnt = %d, p[j] = %d, temp = %d\n", cnt, p[j], temp);
while(temp % p[j] == 0){
temp /= p[j];
// printf("temp = %d, p[j] = %d\n", temp, p[j]);
}
}
}
if(temp != 1) ele[cnt++] = temp;
for(int j = 0 ; j < (1 << cnt) ; j++){
int which = 1;
for(int k = 0 ; k < cnt ; k ++){
if((1 << k) & j)
which *= ele[k];
}
num[which]++;
}
}
// printf("second\n");
LL ans = 0;
for(int i = 0 ; i < n ; i++){
cnt = 0;
int temp = data[i];
for(int j = 0 ; p[j] * p[j] <= temp ; j++){
if(temp % p[j] == 0){
ele[cnt++] = p[j];
while(temp % p[j] == 0) temp /= p[j];
}
}
if(temp != 1) ele[cnt++] = temp;
LL tans = 0;
for(int j = 1 ; j < (1 << cnt) ; j++){
int sta = 0;
LL which = 1;
for(int k = 0 ; k < cnt ; k++){
if((1 << k) & j){
sta++, which *= ele[k];
}
}
if(which > MAXN) continue;
if(sta & 1) tans += num[which];
else tans -= num[which];
}
if(tans > 0) tans--;
ans += tans * (n - 1 - tans);
}
ans = (LL)n * (n - 1) * (n - 2) / 6 - ans / 2;
printf("%I64d\n", ans);
}
return 0;
}