---
# System prepended metadata

title: CSES 2147 Counting Coprime Pairs 題解
tags: [CSES 題解]

---

## [題目連結](https://cses.fi/problemset/task/2417)
先備知識: [狄利克雷卷積](https://oi-wiki.org/math/poly/dgf/#dirichlet-%E5%8D%B7%E7%A7%AF)跟[積性函數](https://oi-wiki.org/math/number-theory/basic/#%E7%A7%AF%E6%80%A7%E5%87%BD%E6%95%B0)

## 題目敘述
給你一組長度為 $n$ 數字，請找出有幾組互質的數？
- $1 \le n \le 10^5$
- $1 \le x_i \le 10^6$

## 解法
### 1. 先將題目問題化成數學式

$\sum_{i=0}^{n} \sum_{j=i+1}^{n} [\gcd(a_i,a_j)=1]$

這邊中括號代表括號內成立時為 $1$，反之為 $0$

在積性函數中有個東西叫 $\epsilon(x)$，正好可以描述這個數學式
所以可以變換成:

$= \sum\limits_{i=0}^{n} \sum\limits_{j=i+1}^{n} \epsilon(\gcd(a_i,a_j))$

因為 $\mu \ast 1 = \epsilon$，可以推得

$= \sum\limits_{i=0}^{n} \sum\limits_{j=i+1}^{n} \sum\limits_{d\mid \gcd(a_i,a_j)} \mu(d)$

變換求和順序，先枚舉 $d\mid \gcd(a_i,a_j)$ 可得

$= \sum_\limits{d=1}^{\infty} \mu(d) \sum_\limits{i=0}^{n} [d\mid a_i] \sum_\limits{j=i+1}^{n} [d\mid a_j]$

觀察一下，可以發現後面兩個 $\sum$ 就是**有幾對可以被 $d$ 整除**

$= \sum_\limits{d=1}^{a_{max}} \mu(d) \cdot \text{有幾對可以被}\ d\ \text{整除}$

### 2. 線性篩莫比烏斯函數
模板:
```cpp=
void init(vector<int> &mobius, int mx) {
    mobius.resize(mx + 1, 0);
    mobius[1] = 1;
    vector<int> wei(mx + 1);  // wei = 0 代表是質數，-1 代表可被平方數整除
    for (int i = 2; i <= mx; i++) {
        if (wei[i] == -1) {
            mobius[i] = 0;
            continue; // 包含平方
        }
        if (wei[i] == 0) {
            wei[i] = 1;
            for (int j = 2; i * j <= mx; j++) {
                if (j % i == 0) wei[i * j] = -1;
                else if (wei[i * j] != -1) wei[i * j]++;
            }
        }
        mobius[i] = wei[i] % 2 == 0 ? 1 : -1;
    }
}
```

### 3. 算有幾對可以被 $d$ 整除
這是排列組合的問題，我們可以把問題簡化成，有幾個可以被 $d$ 整除
直接線性篩是會 $TLE$ 的，複雜度 $O(nx\sqrt{x})$

#### 解法:
對 $x_{max}$ 線性篩，用一個陣列看數字 $x$ 有幾個，這樣會是 $O(x\sqrt{x})$
#### 實作:
```cpp=
int n; cin >> n;
vector<int> v(n), mobius;
for (int i = 0; i < n; i++) {
    cin >> v[i];
}
int mx = *max_element(all(v));
vector<int> p(mx + 1);
init(mobius, mx);
for (auto i : v) p[i]++;
for (int i = 1; i <= mx; i++) {
    for (int j = 2; i * j <= mx; j++) {
        p[i] += p[i * j];
    }
}
```
然後排列組合
```cpp=
for (auto &x : p) x = x * (x - 1) / 2;
```

## [扣](https://cses.fi/paste/4b5793d530e23cfb8d5868/)