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 (0<T100)(0 < T \le 100) the number of test cases, each of the next TT lines contains three integers A,B,NA, B, N where (1AB1015)(1 \le A \le B \le 10^{15}) and (1N109)(1 \le N \le 10^{9}).


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


首先分析問題,我們要求的是區間 [A,B][A, B] 之內 和 NN 互質的數的個數,考慮道題目中的範圍 1AB10151 \le A \le B \le 10^{15},暴力算法是不太可能了。

而在之前的一篇筆記中,我們提到了用埃氏篩法來求 [2,n][2, n] 範圍內的所有質數,主要思想是不斷篩除 [2,n][2, n] 中的非質數和它的倍數,直到列表內只剩下質數爲止。也就是說篩除掉一個區間內的非質數是比較高效的。

順着這個思路我們就可以想到,我們可以求出區間 [2,A1][2, A - 1] 之內和 NN 不互質的數的數量 wA1w_{A - 1} 和 區間 [2,B][2, B] 之內和 NN 不互質的數的數量 wBw_B,計算 $(B - w_B) - (A - 1- w_{A - 1}) $ 就可以求出答案了。

那麼,我們要怎麼求出 wA1w_{A - 1}wBw_B呢,不需要一個個去篩除那麼麻煩,只需要求出 NN 的所有質因數 pip_i,形成一個集合 PP,把 [2,A1][2, A - 1][2,B][2, B] 中所有 PP 的倍數的數量求出來並減去就可以了。

如果 PP 集合中只有一個數 p1p_1 ,我們只需要這樣計算就可以了

wA1=A1p1 w_{A - 1} = \lfloor \frac{A - 1}{p_1} \rfloor

wB=Bp1 w_B = \lfloor \frac{B}{p_1} \rfloor

但很明顯 PP 集合中很可能不僅僅只有一個數,這裏就涉及到容斥原理了。

假設 P={2,3,5}P = \{2, 3, 5\}A=30A = 30B=60B = 60,那麼計算公式如下

wA1=A12+A13+A15A12×3A13×5A12×5+A12×3×5=14+9+5412+0=21 { \begin{aligned} w_{A - 1} =& {\lfloor \frac{A - 1}{2} \rfloor} + {\lfloor \frac{A - 1}{3} \rfloor} + {\lfloor \frac{A - 1}{5} \rfloor} - {\lfloor \frac{A - 1}{2 \times 3} \rfloor} - {\lfloor \frac{A - 1}{3 \times 5} \rfloor} & \\ &{ - \lfloor \frac{A - 1}{2 \times 5} \rfloor} + {\lfloor \frac{A - 1}{2 \times 3 \times 5} \rfloor} &\\ =& 14 + 9 + 5 - 4 - 1 - 2 + 0 &\\ =& 21 &\\ \end{aligned} }

wB=B2+B3+B5B2×3B3×5B2×5+B2×3×5=30+20+121046+2=44 { \begin{aligned} w_B =& {\lfloor \frac{B}{2} \rfloor} + {\lfloor \frac{B}{3} \rfloor} + {\lfloor \frac{B}{5} \rfloor} - {\lfloor \frac{B}{2 \times 3} \rfloor} - {\lfloor \frac{B}{3 \times 5} \rfloor} & \\ &{ - \lfloor \frac{B}{2 \times 5} \rfloor} + {\lfloor \frac{B}{2 \times 3 \times 5} \rfloor} &\\ =& 30 + 20 + 12 - 10 - 4 - 6 + 2 &\\ =& 44 &\\ \end{aligned} }

ans=(BwB)(A1wA1)=8 ans = (B - w_B) - (A - 1 - w_{A - 1}) = 8

這裏又涉及到容斥原理中一個奇加偶減的規律,也就是在迭代計算的過程中 wBw_{B} 要加上 BB 除以奇數個質因數相乘所得的結果,減去 BB 除以偶數個質因數相乘所得的結果。

又有問題出現了,我們要怎麼遍歷所有組合的情況呢? 在最開始提到的位運算與二進制枚舉方法閃亮登場。還是假設 P={2,3,5}P = \{2, 3, 5\},把 PP 的組合情況 GpG_p 用 3 位二進制來表示

Gp=(XXX)B, (X{0,1}) G_p = (XXX)_B,\ (X \in \{0, 1\})

根據排列組合規律,nn 個數的任意個數的組合共有 2n2^n 種情況。

i=0nCni=2n \sum_{i = 0}^n C_n^i = 2^n

我們只需要把 GpG_p00 加到 2n12^n - 1 即可

Gp[(000)B,(111)B] G_p \in [(000)_B, (111)_B]

GpG_p 中某一位置 jj 爲 1,則把 P[j]P[j] 取出與其他爲 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(容斥原理)


聯繫郵箱:[email protected]

Github:https://github.com/CurrenWong

歡迎轉載/Star/Fork,有問題歡迎通過郵箱交流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章