Coprime(容斥定理 + 筛选)

本文探讨了一道算法题目,要求计算从n个人中选择3人使得其ID号两两互质或都不互质的组合数量。通过容斥原理与筛选方法解决了这一问题。

Coprime

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 400    Accepted Submission(s): 178


Problem Description
There are n people standing in a line. Each of them has a unique id number.

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.
 

 

Input
The first line contains an integer T (T ≤ 5), denoting the number of the test cases.

For each test case, the first line contains an integer n(3 ≤ n ≤ 105), denoting the number of people. The next line contains n distinct integers a1, a2, . . . , an(1 ≤ ai ≤ 105) separated by a single space, where ai stands for the id number of the i-th person.
 

 

Output
For each test case, output the answer in a line.
 

 

Sample Input
1
5
1 3 9 10 2
 

 

Sample Output

 

4

 

      题意:

      给出 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;
}

 

 

 

### 关于Codeforces上Coprime问题的解释与解决方案 #### 定义与背景 互质(coprime)是指两个整数的最大公约数为1的情况。对于给定的一组数字,如果其中任意两数之间的最大公约数都是1,则这些数被称为互质。 #### 题目描述 在Codeforces平台上,涉及互质的问题通常会给出一系列操作或条件来判断某些序列中的元素是否满足特定条件下互质的要求。例如,在一些题目中可能会要求找出最长的子序列使得该子序列内的任何一对不同位置上的数值都互质[^1]。 #### 解决方案思路 解决这类问题的一个常见方法是利用埃拉托斯特尼筛法预先计算出一定范围内的素数,并通过预处理的方式快速查询任意两个数之间是否存在大于1的公因数。具体实现可以采用如下步骤: - 使用线性时间复杂度O(n log log n) 的算法构建一个布尔数组`isPrime[]`表示对应索引处的自然数是不是素数; - 对输入数据进行遍历并记录每个数所含有的最小素因子; - 当比较两个数a和b时,只需要检查它们各自对应的最小素因子是否相同即可得出结论;如果不相等则说明这两个数互质。 以下是基于上述逻辑编写的伪代码示例: ```cpp const int MAXN = 1e6; bool isPrime[MAXN]; int minFactor[MAXN]; void sieve() { fill(isPrime, isPrime + MAXN, true); memset(minFactor, 0, sizeof(minFactor)); for(int i = 2; i < MAXN; ++i){ if(!isPrime[i]) continue; for(long long j = (long long)i * i ;j<MAXN;j+=i){ isPrime[j]=false; if (!minFactor[j]) minFactor[j] = i; } minFactor[i] = i; } } ``` 此函数`sieve()`用于初始化全局变量`isPrime[]` 和 `minFactor[]`, 后续可以通过访问`minFactor[a]==minFactor[b]? false:true` 来高效地判定 a,b 是否互质。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值