---
# System prepended metadata

title: ' Thách Thức Lập Trình Xuân Giáp Thìn - Thử thách Vũ Môn'
tags: [VNOJ]

---

---
tags: VNOJ, 
title: Thách Thức Lập Trình Xuân Giáp Thìn - Thử thách Vũ Môn
author: theTai123
---


<style>
    .markdown-body { max-width: 2048px; }
</style>

$\Huge \text{🐲 Thách Thức Lập Trình Xuân Giáp Thìn - Thử thách Vũ Môn
}$

-----

###### 🔗 Link: [https://oj.vnoi.info/problem/dovui_2024_a](https://oj.vnoi.info/problem/dovui_2024_a)
###### 📌 Tags: `Math`,`Brute-force`,`Sieve`
###### 👤 Writer: @theTai123
###### 📋 Content: 

[TOC]

_____
## Tóm Tắt

Cho một dãy số gồm $n$ số nguyên dương $a_1,a_2,...,a_n$. Với mỗi số nguyên dương $a_i$ đếm xem có bao nhiêu nghiệm nguyên dương thỏa mãn phương trình $x+y+gcd(x,y)=a_i$. $(1\leq n \leq2\cdot10^5,1\leq a_i \leq4\cdot10^6)$.

*Từ đây, gọi $m$ là $\max (a_i$)*.




-----
## Subtask 1

> **Tags:** `Brute-force`
> [color=lightgreen]

Ở subtask này, ta vét cạn tất cả các cặp $(x,y)$ nguyên dương và kiểm tra có thỏa mãn không.

> **Complexity:** $O(n \cdot m^2)$
> [color=lightgreen]

:::success
:::spoiler Code của theTai123 
```cpp=
#include <iostream>
#include <algorithm>
#define pb push_back
#define pop pop_back
#define fi first
#define se second
typedef long long ll;
typedef double db;
template<typename A, typename B> bool minimize(A& a, const B& b) {return a > b ? a = b, 1 : 0;}
template<typename A, typename B> bool maximize(A& a, const B& b) {return a < b ? a = b, 1 : 0;}

using namespace std;
int n;
int res;

void process(int x) {
    res = 0;
    for(int i = 1; i < x; i++) {
        for(int j = 1; j < x; j++) {
            if(i + j + __gcd(j, i) == x) res++;
        }
    }
    cout << res << " ";
}

int main() {
    ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    cin >> n;
    for(int i = 1; i <= n; i++) {
        int x; cin >> x;
        process(x);
    }
    return 0;
}

```
:::

------
## Subtask 2

>**Tags:** `Math` `Brute-force`
> [color=lightgreen]

Từ phương trình $x+y+gcd(x,y)=a_i$ ,ta đặt $gcd(x,y)=q$. Vì $x$ và $y$ chia hết cho $q$ nên ta có $x=aq, y=bq$ $(a,b\in\mathbb N^*)$.

Suy ra: 
$aq+bq+q=a_i$

$\Leftrightarrow  q \cdot (a+b+1)=a_i$

$\Leftrightarrow  a+b+1={a_i \over q}$

$\Leftrightarrow  a+b={a_i \over q}-1$.

Để $a+b \in \mathbb N^*$ thì ${a_i \over q} -1\in \mathbb N^*$ suy ra $q \in Ư(a_i)$  và $q \neq a_i$.

Nếu $gcd(aq,bq)=q$ thì $gcd(a,b)=1$ vậy $a$ và $b$ là hai số nguyên tố cùng nhau.

Do đó nhiệm vụ của ta là đếm tất cả các cặp $a,b \in \mathbb N^*$ sao cho $a+b={a_i \over q}-1$ với $q \in Ư(a_i), q \neq a_i$ và $gcd(a,b)=1$.
    
> **Complexity:** $O(n \cdot m \log m)$
> [color=lightgreen]    

:::success
:::spoiler Code của theTai123 
```cpp=
#include <iostream>
#include <algorithm>
#define pb push_back
#define pop pop_back
#define fi first
#define se second
typedef long long ll;
typedef double db;
template<typename A, typename B> bool minimize(A& a, const B& b) {return a > b ? a = b, 1 : 0;}
template<typename A, typename B> bool maximize(A& a, const B& b) {return a < b ? a = b, 1 : 0;}

using namespace std;
int n;

int get(int t) {
    int res = 0;
    for(int j = 1; j < t; j++) {
        if(__gcd(j, t - j) == 1) {
            res++;
        }
    }
    return res;
}

void process(int x) {
    int res = 0;
    for(int i = 1; i < x; i++) {
        if(x % i == 0) res += get(x / i - 1);
    }
    cout << res << " ";
}

int main() {
    ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    cin >> n;
    for(int i = 1; i <= n; i++) {
        int x; cin >> x;
        process(x);
    }
    return 0;
}

```
:::

------
## Subtask 3

> **Tags:** `Math` 
> [color=lightgreen]

Từ subtask 2, gọi ${a_i\over q}-1$ là $u$. Ta thấy có đúng $u-1$ cặp số $a,b$ thõa mãn $a+b=u$.

Với mỗi cặp $a,b$ bất kì, nếu $gcd(a,u)=1$ thì $gcd(a,u-a)=1$ hay $gcd(a,b)=1$. Vậy nên ta chỉ cần đếm tất cả các số nguyên tố cùng nhau với $u$ trong đoạn $[1;u-1]$ và ta có thể dễ dàng đếm được bằng cách sử dụng phi hàm Euler $ϕ$ với độ phức tạp $O(\sqrt m)$.

> **Complexity:** $O(n \cdot m \log \sqrt m)$
> [color=lightgreen]

:::success
:::spoiler Code của theTai123 
```cpp=
#include <iostream>
#include <math.h>
#define pb push_back
#define pop pop_back
#define fi first
#define se second
typedef long long ll;
typedef double db;
template<typename A, typename B> bool minimize(A& a, const B& b) {return a > b ? a = b, 1 : 0;}
template<typename A, typename B> bool maximize(A& a, const B& b) {return a < b ? a = b, 1 : 0;}

using namespace std;
int n;

int phi(int k) {
    if(k == 0) return 0;
    int res = k - 1;
    for (int i = 2; i * i <= k; ++i) {
        if (k % i == 0) {
            while(k % i == 0) {
                k /= i;
            }
            res -= res / i;
        }
    }
    if (k > 1) {
        res -= res / k;
    }
    return res;
}

void process(int x) {
    int res = 0;
    for(int i = 1; i < x; ++i) {
        if(x % i == 0) res += phi(x / i - 1);
    }
    cout << res << " ";
}

int main() {
    ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    cin >> n;
    for(int i = 1; i <= n; i++) {
        int x; cin >> x;
        process(x);
    }
    return 0;
}

```
:::

------
## Subtask 4

> **Tags:** `Math` `Sieve`
> [color=lightgreen]

Tương tự như subtask 3 nhưng thay vào đó ta sẽ sử dùng sàng thay vì phải tính lại kết quả.
Độ phức tạp khi sàng là $O(m \log \log m)$.

Để tăng tốc, thay vì kiểm tra ước từ 1 tới $m$, ta sẽ kiểm tra từ 1 tới $\sqrt m$ và kiểm tra xem $m\over i$ có phải ước của $m$ không.

> **Complexity:** $O(n \cdot \sqrt m + m \log \log m)$
> [color=lightgreen]

:::success
:::spoiler Code của theTai123
```cpp=
#include <iostream>
#include <math.h>
#define pb push_back
#define pop pop_back
#define fi first
#define se second
#define MAXA 4000000
typedef long long ll;
typedef double db;
template<typename A, typename B> bool minimize(A& a, const B& b) {return a > b ? a = b, 1 : 0;}
template<typename A, typename B> bool maximize(A& a, const B& b) {return a < b ? a = b, 1 : 0;}

using namespace std;
int n, res;
int phi[MAXA + 7];

void sieve() {
    for(int i = 1; i <= MAXA + 5; i++) {
        phi[i] = i - 1;
    }
    for(int i = 2; i <= MAXA + 5; i++) {
        if(phi[i] == i - 1) {
            for(int j = i; j <= MAXA + 5; j += i) {
                phi[j] -= phi[j] / i;
            }
        }
    }
}

void process(int x) {
    res = 0;
    for(int i = 1; i * i < x; i++) {
        if(x % i == 0) {
            int u = x / i;
            res += phi[u - 1];
            if(x % u == 0) res += phi[x / u - 1];
        }
    }
    int u = sqrt(x);
    if(u * u == x) res += phi[u - 1];
    cout << res << " ";
}

int main() {
    ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);
    cin >> n;
    int x;
    sieve();
    for(int i = 1; i <= n; i++) {
        cin >> x;
        process(x);
    }
    return 0;
}

```
:::
