Coprime
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 400 Accepted Submission(s): 178
Now the Ragnarok is coming. We should choose 3 people to defend the evil. As a group, the 3 people should be able to communicate. They are able to communicate if and only if their id numbers are pairwise coprime or pairwise not coprime. In other words, if their id numbers are a, b, c, then they can communicate if and only if [(a, b) = (b, c) = (a, c) = 1] or [(a, b) ≠ 1 and (a, c) ≠ 1 and (b, c) ≠ 1], where (x, y) denotes the greatest common divisor of x and y.
We want to know how many 3-people-groups can be chosen from the n people.
For each test case, the first line contains an integer n(3 ≤ n ≤ 10 5), denoting the number of people. The next line contains n distinct integers a 1, a 2, . . . , a n(1 ≤ a i ≤ 10 5) separated by a single space, where a i stands for the id number of the i-th person.
题意:
给出 T 组样例,每组样例都有个 N,代表有 N 个数,问能有多少种 3 元组组合,使其 3 个任意两者之间互质或者不互质。
思路:
容斥定理 + 筛选。问题的逆命题就是,三者之间的组合存在的关系的:互质,互质,不互质;不互质,不互质,互质。而对于一个数来说,互质 + 不互质 + 1 = n,所以转换为求一个数互质与不互质的个数分别是多少。且通过求互质的数,就可以得出不互质的数是多少。得出这样的结论之后,问题又转化成了求一个数互质的数共有几个。
首先通过筛选的方法,求出每个因子所构成的数在数列中一共有几个,用 ans 数组保存。之后在统计一个数与之互质的总个数时,也可以运用容斥定理求出。
比如 30 的质因子是 2,3,5,那么所求的总数就是 ans [ 2 ] + ans [ 3 ] + ans [ 5 ] - ans [ 2 * 3 ] - ans [ 2 * 5 ] - ans [ 3 * 5 ] + ans [ 2 * 3 * 5 ](如果质因子个数为 n 个,可发现,当为偶数个质因子构成的合数时,总数需要减,而当为奇数时,则需要加,其实就是容斥定理)。
每个数都这样求出来之后加和后,总数还要 / 2,因为一个数被同时算了两次,最后用 C ( n, 3 ) - res 即为所求。记得用 long long 即可。
求每个因子所构成的数在数列中的个数要用筛选的方法,不然会 TLE。
AC:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int N = 100005; int n; int num[N], ans[N], pri[N]; bool vis[N]; void init () { memset(ans, 0, sizeof(ans)); memset(vis, 0, sizeof(vis)); } bool test (int x) { if (x == 1) return false; for (int i = 2; i * i <= x; ++i) { if (!(x % i)) return false; } return true; } ll solve () { ll res = 0; for (int i = 0; i < n; ++i) { int now = num[i], sat = 0, sum = 0; int m = 2; while (m * m <= now) { if (!(now % m)) { pri[sum++] = m; while (!(now % m)) now /= m; } ++m; } if (test(now)) pri[sum++] = now; for (int j = 1; j < (1 << sum); ++j) { int sum1 = 0, temp = 1; for (int k = 0; k < sum; ++k) { if ((1 << k) & j) { ++sum1; temp *= pri[k]; } } if (sum1 & 1) sat += ans[temp]; else sat -= ans[temp]; } if (!sat) continue; res += (ll)(sat - 1) * ((ll)n - sat); } return res / 2; } int main () { int t; scanf("%d", &t); while (t--) { scanf("%d", &n); init(); for (int i = 0; i < n; ++i) { scanf("%d", &num[i]); vis[ num[i] ] = 1; } for (int i = 2; i < N; ++i) { for (int j = i; j < N; j += i) { if (vis[j]) ++ans[i]; } } ll res = (ll)n * (n - 1) * (n - 2) / 6; printf("%I64d\n", res - solve()); } return 0; }