Skip to content

HDU 4135 Co-prime#

目录#

1. 题目描述#

1.1. Limit#

Time Limit: 1000 ms

Memory Limit: 32768 kB

1.2. Problem Description#

Given a number N, you are asked to count the number of integers between A and B inclusive which are relatively prime to N.

Two integers are said to be co-prime or relatively prime if they have no common positive divisors other than 1 or, equivalently, if their greatest common divisor is 1. The number 1 is relatively prime to every integer.


1.3. Input#

The first line on input contains T the number of test cases, each of the next lines contains three integers where and .


1.4. Output#

For each test case, print the number of integers between A and B inclusive which are relatively prime to N. Follow the output format below.


1.5. Sample Input#

2
1 10 2
3 15 5

1.6. Sample Output#

Case #1: 5
Case #2: 10

In the first test case, the five integers in range [1,10] which are relatively prime to 2 are {1,3,5,7,9}.

1.7. Source#

The Third Lebanese Collegiate Programming Contest


2. 解读#

这道题我们需要使用到的是容斥原理

容斥原理分为三种实现1

1.位运算与二进制枚举(容易理解)

2.队列数组(耗时最短)

3.递归(代码最短但不容易理解)

在这里我们主要讨论比较好理解的位运算与二进制枚举方法,其他两种方法可以参考深海沧澜夜未央的博客2


首先分析问题,我们要求的是区间 之内 和 互质的数的个数,考虑道题目中的范围 ,暴力算法是不太可能了。

而在之前的一篇笔记中,我们提到了用埃氏筛法来求 范围内的所有质数,主要思想是不断筛除 中的非质数和它的倍数,直到列表内只剩下质数为止。也就是说筛除掉一个区间内的非质数是比较高效的。

顺着这个思路我们就可以想到,我们可以求出区间 之内和 不互质的数的数量 和 区间 之内和 不互质的数的数量 ,计算 就可以求出答案了。

那么,我们要怎么求出 呢,不需要一个个去筛除那么麻烦,只需要求出 的所有质因数 ,形成一个集合 ,把 中所有 的倍数的数量求出来并减去就可以了。

如果 集合中只有一个数 ,我们只需要这样计算就可以了

但很明显 集合中很可能不仅仅只有一个数,这里就涉及到容斥原理了。

假设 ,那么计算公式如下

这里又涉及到容斥原理中一个奇加偶减的规律,也就是在迭代计算的过程中 要加上 除以奇数个质因数相乘所得的结果,减去 除以偶数个质因数相乘所得的结果。

又有问题出现了,我们要怎么遍历所有组合的情况呢? 在最开始提到的位运算与二进制枚举方法闪亮登场。还是假设 ,把 的组合情况 用 3 位二进制来表示

根据排列组合规律, 个数的任意个数的组合共有 种情况。

我们只需要把 加到 即可

中某一位置 为 1,则把 取出与其他为 1 的质因数相乘即可遍历所有组合情况。

3. 代码#

#include <algorithm>
#include <iostream>
#include <string.h>
using namespace std;
typedef long long ll;

// 存储质因数
ll prime[100];
// 质因数计数
int cnt;

// 计算质因数
void init(int m)
{
    cnt = 0;
    for (ll i = 2; i * i < m; i++)
        // 若为因数
        if (m % i == 0) {
            //prime储存素因子,cnt为素因子的个数
            prime[cnt++] = i;
            // 将这个质因数从m中除去,防止计算到质因数的倍数
            while (m % i == 0) {
                m /= i;
            }
        }
    //这里是因为有的n的因子大于sqrt(n),比如14,他的素因子有2,7
    if (m > 1)
        prime[cnt++] = m;
}

// 容斥原理计算[2, cur] 中不与 N 互斥的数的数量
ll IE_Principle(ll cur)
{
    // 存储质因数组合的乘积
    ll res = 0;
    // 存储结果
    ll ans = 0;
    // 从 0 遍历到 2^cnt - 1
    for (ll i = 1; i < ll(1 << cnt); i++) {
        res = 1;
        ll flag = 0;
        // 遍历所有数位
        for (ll j = 0; j < cnt; j++) {
            //出现因子
            if (i & (ll(1 << j))) {
                // 统计出现的集合个数
                flag++;
                // 取并之后的因子乘积
                res *= prime[j];
            }
        }
        // 判断组合中因子个数的奇偶性
        if (flag & 1) {
            // 若为奇数 加
            ans += cur / res;
        } else {
            // 若为偶数 减
            ans -= cur / res;
        }
    }
    return ans;
}
int main()
{
    // test case
    ll t;
    cin >> t;
    // 计数
    int icase = 0;
    // 存储 A B N
    ll a, b, n;
    // 存储结果
    ll ans;
    // 对每一个test case进行遍历
    while (t--) {
        // 初始化
        memset(prime, 0, sizeof(prime));
        // 输入
        cin >> a >> b >> n;
        // 求质因数
        init(n);
        // 使用容斥原理进行计算并输出
        ans = b - IE_Principle(b) - (a - 1 - IE_Principle(a - 1));
        printf("Case #%d: %lld\n", ++icase, ans);
    }
    return 0;
}

代码参考自深海沧澜夜未央的博客2,思路借鉴了生如夏花的博客3

4. 参考文献#

[1] 深海沧澜夜未央. CSDN博客. 容斥原理(组合数学)总结

[2] 深海沧澜夜未央. CSDN博客. HDU4135 Co-prime【容斥原理】3方法

[3] 生如夏花. 博客园博客. hdu 4135(容斥原理)


联系邮箱:curren_wong@163.com

Github:https://github.com/CurrenWong

欢迎转载/Star/Fork,有问题欢迎通过邮箱交流。

Back to top