---
# System prepended metadata

title: Project Euler

---

**Write-up** [Project Euler](https://projecteuler.net)
---

**Problem 169: Sums of Powers of Two** - 99,99999%
---
* Đề [Problem 169](https://projecteuler.net/problem=169)

![](https://hackmd.io/_uploads/r1Kl8vmfT.png)

* Sau khi chạy trâu các số từ 1 -> 20 và thử submit lên [oeis.org](https://oeis.org/) thì được kết quả là dãy [Stern's Diatomic Series](https://mathworld.wolfram.com/SternsDiatomicSeries.html). Do đó có công thức truy hồi số cách biểu diễn của số n là:
    * $a_{n}$ = $\begin{cases} a_{n / 2} \ \ \ \ \  \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ n \ chẵn \\ a_{(n + 1) / 2} + a_{(n - 1) / 2} \ \ \ \ \ n \ lẻ \end{cases}$
* Tuy nhiên vì dãy Stern's Diatomic Series bắt đầu đếm từ 0, còn dãy ta cần tính bắt đầu từ 1 nên khi tính, để ra kết quả chính xác thì ta tính $n+1$ sẽ được kết quả của số $n$
* Nhìn vào có thể hoàn toàn cài đặt bằng đệ quy, tuy nhiên số cần tính là 10^25^, vượt quá kiểu long long trong C++ nhưng vẫn có thể tính toán trong Python 1 cách bình thường
```python
num = {0: 1, 1: 1}

def cal(n):
    if n in num:
        return num[n]
    if n % 2 == 1:
        num[n] = cal((n + 1) // 2) + cal((n - 1) // 2)
        return num[n]
    else:
        num[n] = cal(n // 2)
        return num[n]


print(cal(10 ** 25 + 1))
```
=> 178653872807

**Problem 185: Number Mind** - 100%
---
* Đề: [Problem 185](https://projecteuler.net/problem=185)
![](https://hackmd.io/_uploads/ryAvHHGMa.png)
* Đề chỉ yêu cầu đơn giản là tìm ra số n có 16 số thỏa các điều kiện các chữ số đúng với các số cho sẵn ban đầu
* Vì số có 16 chữ số là 10^16, nếu sinh 10^16 số và duyệt thì đợi rất lâu, vậy nên ý tưởng duyệt trâu từng điều kiện, giả sử ở số s hiện tại k số đúng là s~1~, s~2~,.. s~k~ thì ở các số tiếp theo sẽ cài đặt sao cho n - k số còn lại đều không đúng
* Thử chạy trâu khi chưa xác định được độ phức tạp thì vô tình có kết quả trong khoảng 10s
    * Hoàn toàn cài theo yêu cầu của đề, k số đúng thì n - k số còn lại không đúng, nếu những số đã đúng trùng với số đang xét thì số đang xét cũng đúng - hoàn toàn quay lui không có gì thêm
```cpp
#include<bits/stdc++.h>
using namespace std;

vector<pair<int, string>> num;
string b;
int vis[20][10], a;
int patch[20];
int n_patch[20];
bool ret = false;

bool check()
{
    for(int i = 0; i < 16; i++)
    {
        if(!patch[i]) continue;
        if(vis[i][n_patch[i]]) return false;
    }
    return true;
}

bool f_ans()
{
    for(int i = 0; i < 16; i++)
    {
        if(patch[i] && vis[i][n_patch[i]]) return false;
        bool tmp = false;
        for(int j = 0 + (i == 0); j < 10; j++)
            tmp |= !(vis[i][j]);
        if(!tmp) return false;
    }
    for(int i = 0; i < 16; i++)
    {
        if(patch[i]) cout << n_patch[i];
        else
            for(int j = 0 + (i == 0); j < 10; j++)
                if(!vis[i][j])
                {
                    cout << j;
                    break;
                }
    }
    return true;
}

void solve(int k)
{
    if (ret) return;
    if(k == num.size())
    {
        ret = f_ans();
        return;
    }
    string tmp = num[k].second;
    int u = num[k].first;
    if(u == 0)
    {
        for(int i = 0; i < tmp.size(); i++)
            vis[i][tmp[i] - '0']++;
        solve(k + 1);
        return;
    }
    if(u == 1)
    {
        for(int i = 0; i < tmp.size(); i++)
        {
            if(ret) return;
            if(vis[i][tmp[i] - '0']) continue;
            if(patch[i] && n_patch[i] != (tmp[i] - '0')) continue;
            patch[i]++;
            n_patch[i] = tmp[i] - '0';

            bool atmp = true;
            for(int j = 0; j < tmp.size(); j++)
            {
                if(i == j) continue;
                if(patch[j] && n_patch[j] == (tmp[j] - '0'))
                {
                    atmp = false;
                    break;
                }
            }

            if(atmp)
            {
                for(int j = 0; j < tmp.size(); j++)
                {
                    if(j == i) continue;
                    vis[j][tmp[j] - '0']++;
                }

                solve(k + 1);

                for(int j = 0; j < tmp.size(); j++)
                {
                    if(j == i) continue;
                    vis[j][tmp[j] - '0']--;
                }
            }
            patch[i]--;
        }
        return;
    }
    if(u == 2)
    {
        for(int i = 0; i < tmp.size(); i++)
            for(int x = i + 1; x < tmp.size(); x++)
            {
                if(ret) return;

                if(vis[i][tmp[i] - '0'] || vis[x][tmp[x] - '0']) continue;
                if((patch[i] && n_patch[i] != (tmp[i] - '0')) || (patch[x] && n_patch[x] != (tmp[x] - '0'))) continue;
                patch[i]++;
                patch[x]++;
                n_patch[i] = tmp[i] - '0';
                n_patch[x] = tmp[x] - '0';

                bool atmp = true;
                for(int j = 0; j < tmp.size(); j++)
                {
                    if(i == j) continue;
                    if(x == j) continue;
                    if(patch[j] && n_patch[j] == (tmp[j] - '0'))
                    {
                        atmp = false;
                        break;
                    }
                }

                if(atmp)
                {
                    for(int j = 0; j < tmp.size(); j++)
                    {
                        if(j == i) continue;
                        if(j == x) continue;
                        vis[j][tmp[j] - '0']++;
                    }

                    solve(k + 1);

                    for(int j = 0; j < tmp.size(); j++)
                    {
                        if(j == i) continue;
                        if(j == x) continue;
                        vis[j][tmp[j] - '0']--;
                    }
                }
                patch[i]--;
                patch[x]--;
            }
        return;
    }

    if(u == 3)
    {
        for(int i = 0; i < tmp.size(); i++)
            for(int x = i + 1; x < tmp.size(); x++)
                for(int y = x + 1; y < tmp.size(); y++)
                {
                    if(ret) return;

                    if(vis[i][tmp[i] - '0'] || vis[x][tmp[x] - '0'] || vis[y][tmp[y] - '0']) continue;
                    if((patch[i] && n_patch[i] != (tmp[i] - '0')) || (patch[x] && n_patch[x] != (tmp[x] - '0')) || (patch[y] && n_patch[y] != (tmp[y] - '0'))) continue;
                    patch[i]++;
                    patch[x]++;
                    patch[y]++;
                    n_patch[i] = tmp[i] - '0';
                    n_patch[x] = tmp[x] - '0';
                    n_patch[y] = tmp[y] - '0';

                    bool atmp = true;
                    for(int j = 0; j < tmp.size(); j++)
                    {
                        if(i == j) continue;
                        if(x == j) continue;
                        if(y == j) continue;
                        if(patch[j] && n_patch[j] == (tmp[j] - '0'))
                        {
                            atmp = false;
                            break;
                        }
                    }

                    if(atmp)
                    {
                        for(int j = 0; j < tmp.size(); j++)
                        {
                            if(j == i) continue;
                            if(j == x) continue;
                            if(j == y) continue;
                            vis[j][tmp[j] - '0']++;
                        }

                        solve(k + 1);

                        for(int j = 0; j < tmp.size(); j++)
                        {
                            if(j == i) continue;
                            if(j == x) continue;
                            if(j == y) continue;
                            vis[j][tmp[j] - '0']--;
                        }
                    }
                    patch[i]--;
                    patch[x]--;
                    patch[y]--;
                }
            return;
    }
}

int main()
{
    ios_base::sync_with_stdio(false); cin.tie(0);

    freopen("text.txt", "r", stdin);

    while(cin >> a >> b)
        num.push_back({a, b});
    sort(num.begin(), num.end());
    solve(0);
}
```
![](https://hackmd.io/_uploads/BJbU8HzMT.png)


**Problem 231 : Prime factorisation of binomial coefficients** - 100%
---
* Đề: [Problem 231](https://projecteuler.net/problem=231)

![](https://hackmd.io/_uploads/SyihKpo-T.png)

- Phân tích tổ hợp chập nCk thành tích các thừa số nguyên tố có dạng 2^a^x3^b^x5^c^..., sau đó tính tổng của 2a+3b+5c+...
    * Vì trong công thức tính nCk là các giai thừa nên nó sẽ là tích tất cả các số <= n, vậy nên toàn bộ ước nguyên tố lẫn lũy thừa của ước nguyên tố đều sẽ được tính
    * Do đó có thể sử dụng phân tích n! thành tích nguyên tố để đếm trong độ phức tạp O(log~e~n), dùng sàng nguyên tố để prebuild các số nguyên tố trong khoảng từ 2 -> n sau đó tính toán để giảm độ phức tạp
        * Hoàn toàn có thể chạy trong thời gian nhỏ vì độ phức tạp không quá lớn: O(nlog~e~n) với n = 2e7
```cpp
#include<bits/stdc++.h>
using namespace std;
#define N (long long)2e7
#define M (long long)15e6

bool prime[N + 5];
vector <long long> num;
void build()
{
    for(int i = 2; i * i <= N; i++)
    {
        if(prime[i]) continue;
        for(int j = i * i; j <= N; j += i)
            prime[j] = true;
    }
    for(int i = 2; i <= N; i++)
        if(!prime[i])
            num.push_back(i);
}

long long cal(long long k)
{
    long long ret = 0;
    long long c_p, tmp;
    for(int i = 0; i < num.size(); i++)
    {
        tmp = num[i];
        while(tmp <= k)
        {
            ret += num[i] * (k / tmp);
            c_p++;
            tmp *= num[i];
        }
    }
    return ret;
}

long long n, m;
int main()
{
    ios_base::sync_with_stdio(false); cin.tie(0);
    build();
    cout << cal(N) - (cal(M) + cal(N - M));
}
```
![](https://hackmd.io/_uploads/SJadXfMGT.png)

**Problem 237: Tours on a 4xN Playing Board** - 100%
---
* Đề: [Problem 237](https://projecteuler.net/problem=237)
![](https://hackmd.io/_uploads/ryOFUffM6.png)

* Biết được bài toán có dạng quy hoạch động nên tìm công thức quy hoạch động bằng cách:
    *  Chạy trâu quay lui tìm đường đi trên bảng, sinh 10 giá trị đầu của T(n)
```cpp
#include <bits/stdc++.h>
using namespace std;
#define N 20

int num[5][N + 5];
int ans;
bool vis[5][N + 5];
int x[5]{0, 0, 1, -1};
int y[5]{1, -1, 0, 0};

void atmp(int n, int i, int j, int s)
{
    if(s == n * 4 - 1 && i == 4 && j == 1)
    {
        ans++;
        return;
    }
    for(int k = 0; k < 4; k++)
    {
        int u = i + x[k];
        int v = j + y[k];
        if(u < 1 || u > 4) continue;
        if(v < 1 || v > n) continue;
        if(vis[u][v]) continue;
        vis[u][v] = true;
        atmp(n, u, v, s + 1);
        vis[u][v] = false;
    }
}

int main()
{
    vis[1][1] = true;
    for(int i = 1; i <= 10; i++)
    {
        ans = 0;
        atmp(i, 1, 1, 0);
        cout << ans << " ";
    }
    return 0;
}
```

![](https://hackmd.io/_uploads/S1Yt78GMp.png)
* =>  Sau đó đưa dãy số vào [oeis.org](https://oeis.org/ ) tìm được công thức truy hồi:
    * $a_{n}$ = $a_{n-4}$ - $2a_{n-3}$ + $2a_{n-2}$ + $2a_{n-1}$
* Tuy nhiên n lớn (10^12^) nên quy đổi bài toán sang nhân ma trận để tính lũy thừa trong độ phức tạp log, giảm thời gian chờ kết quả:
    * Ma trận ban đầu $\begin{bmatrix} a_{1} \ \ \ a_{2} \ \ \ a_{3} \ \ \ a_{4}\end{bmatrix}$ = $\begin{bmatrix} 1 \ \ \ 1 \ \ \ 4 \ \ \ 8 \end{bmatrix}$
    * Ma trận cơ sở: $\begin{bmatrix} 0 \ \ \ 0 \ \ \ 0 \ \ \ \  1 \\ 1 \ \ \ 0 \ \ \ 0 \ {-2} \\ 0 \ \ \ 1 \ \ \ 0 \ \ \ 2 \\ 0 \ \ \ 0 \ \ \ 1 \ \ \ 2 \end{bmatrix}$
    * Phép nhân ma trận:
    $\begin{bmatrix} a_{n - 3} \ \ \ a_{n - 2} \ \ \ a_{n - 1} \ \ \ a_{n}\end{bmatrix}$ * $\begin{bmatrix} 0 \ \ \ 0 \ \ \ 0 \ \ \ \  1 \\ 1 \ \ \ 0 \ \ \ 0 \ {-2} \\ 0 \ \ \ 1 \ \ \ 0 \ \ \ 2 \\ 0 \ \ \ 0 \ \ \ 1 \ \ \ 2 \end{bmatrix}$ = $\begin{bmatrix} a_{n - 2} \ \ \ a_{n - 1} \ \ \ a_{n} \ \ \ a_{n + 1}\end{bmatrix}$
    
        * => $\begin{bmatrix} a_{n - 3} \ \ \ a_{n - 2} \ \ \ a_{n - 1} \ \ \ a_{n}\end{bmatrix}$ * $\begin{bmatrix} 0 \ \ \ 0 \ \ \ 0 \ \ \ \  1 \\ 1 \ \ \ 0 \ \ \ 0 \ {-2} \\ 0 \ \ \ 1 \ \ \ 0 \ \ \ 2 \\ 0 \ \ \ 0 \ \ \ 1 \ \ \ 2 \end{bmatrix} ^k$ = $\begin{bmatrix} a_{n - 3 + k} \ \ \ a_{n - 2 + k} \ \ \ a_{n - 1 + k} \ \ \ a_{n + k}\end{bmatrix}$
    
    Kết quả cuối cùng sẽ là lấy ma trận ban đầu nhân cột 4 của ma trận $\begin{bmatrix} 0 \ \ \ 0 \ \ \ 0 \ \ \ \  1 \\ 1 \ \ \ 0 \ \ \ 0 \ {-2} \\ 0 \ \ \ 1 \ \ \ 0 \ \ \ 2 \\ 0 \ \ \ 0 \ \ \ 1 \ \ \ 2 \end{bmatrix} ^{10^{12}-4}$
        * Độ phức tạp nhỏ: nhân ma trận O(4x4x4) x lũy thừa tốn O(log~2~(N)) (N = 10^12^) = tổng thể độ phức tạp sẽ là O(12xlog~2~(N))
 
```cpp
#include <bits/stdc++.h>
using namespace std;
#define N (long long)1e12
#define M (long long)1e8

long long a[5]{0, 1, 1, 4, 8};

struct mat{
    long long num[5][5]{{},{},{},{},{}};
};

mat Base;

mat mul(mat a, mat b)
{
    mat ret;
    for(int i = 1; i <= 4; i++)
        for(int j = 1; j <= 4; j++)
            for(int k = 1; k <= 4; k++)
            {
                ret.num[i][j] += a.num[i][k] * b.num[k][j];
                ret.num[i][j] %= M;
            }
    return ret;
}

mat m_pow(mat a, long long b)
{
    if(b == 1) return a;
    mat c = m_pow(a, b / 2);
    mat k = mul(c, c);
    if ((b % 2))
        k = mul(k, a);
    return k;
}

long long ans()
{
    long long ret = 0;
    for(int i = 1; i <= 4; i++)
    {
        ret += a[i] * Base.num[i][4];
        ret %= M;
    }
    return ret % M;
}

int main()
{
    Base.num[1][4] = Base.num[2][1] = Base.num[3][2] = Base.num[4][3] = 1;
    Base.num[3][4] = Base.num[4][4] = 2;
    Base.num[2][4] = -2;
    Base = m_pow(Base, N - 4);
    cout << ans();
}
```

![](https://hackmd.io/_uploads/H1X_V7fM6.png)

**Problem 247: Squares Under a Hyperbola**
---
* Đề: [Prblem 247](https://projecteuler.net/problem=247)

![](https://hackmd.io/_uploads/SkRiUvQMT.png)
