Hướng dẫn giải của Đếm dãy chia hết cho 7


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.

Lời giải này đang bị ẩn cho đến khi bạn chọn mở ra.

Chúng tôi khuyên bạn nên tự thử giải bài trước. Việc mở lời giải có thể làm lộ mất ý tưởng chính trước khi bạn có cơ hội tự giải.

Bạn phải đăng nhập để mở lời giải này.

Đăng nhập

Tác giả: Hiếu Nguyễn, dainghiajustiin, phucduy08, nguyenlegiang27042000

Hiểu bài toán

Cho một số nguyên dương n. Yêu cầu đếm số lượng dãy số a1, a2, ..., an sao cho mỗi phần tử ai đều nằm trong khoảng [1, 7] (tức là a_i ∈ {1, 2, 3, 4, 5, 6, 7}) và tổng của tất cả các phần tử trong dãy chia hết cho 7. Kết quả cần in ra số dư của số lượng dãy tìm được khi chia cho 10^9 + 7. Với n rất lớn (có thể lên tới 10^18), ta cần một giải pháp hiệu quả về mặt thời gian.

Các cách tiếp cận

Cách Quy hoạch động kết hợp Ma trận (Matrix Exponentiation)
#include <bits/stdc++.h>
using namespace std;
const long long MOD = 1000000007;
const int SIZE = 7;

// Nhân hai ma trận modulo MOD
vector<vector<long long>> mat_mult(const vector<vector<long long>>& A,
                                   const vector<vector<long long>>& B) {
    vector<vector<long long>> result(SIZE, vector<long long>(SIZE, 0));
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            for (int k = 0; k < SIZE; k++) {
                result[i][j] = (result[i][j] + A[i][k] * B[k][j]) % MOD;
            }
        }
    }
    return result;
}

// Lũy thừa ma trận
vector<vector<long long>> mat_pow(vector<vector<long long>> base, long long exp) {
    vector<vector<long long>> result(SIZE, vector<long long>(SIZE, 0));
    for (int i = 0; i < SIZE; i++) result[i][i] = 1; // ma trận đơn vị
    while (exp > 0) {
        if (exp & 1) result = mat_mult(result, base);
        base = mat_mult(base, base);
        exp >>= 1;
    }
    return result;
}

int main() {
    long long n;
    cin >> n;
    if (n == 0) {
        cout << 0 << endl;
        return 0;
    }
    // Ma trận chuyển trạng thái
    // M[i][j] là số cách chuyển từ tổng chia hết cho 7 dư i sang tổng chia hết cho 7 dư j
    // Khi thêm một số x (1..7), tổng mới là (i + x) % 7
    // Với mỗi i, có 7 giá trị x, mỗi x đưa ta đến (i+x)%7.
    // Do đó M[i][(i+x)%7]++ cho mỗi x.
    // Ma trận này đều có giá trị 1.
    vector<vector<long long>> M(SIZE, vector<long long>(SIZE, 1));

    // Tính M^(n-1)
    vector<vector<long long>> Mn = mat_pow(M, n - 1);

    // Ban đầu với n=1, ta có 1 phần tử, tổng là a_1.
    // Để tổng chia hết cho 7 (dư 0), ta cần chọn a_1 chia hết cho 7.
    // Trong khoảng [1,7], chỉ có số 7 chia hết cho 7.
    // Vậy vector trạng thái ban đầu (n=1) là: [0, 0, 0, 0, 0, 0, 1]
    // (dư 0,...,5 có 0 cách, dư 6 có 1 cách).

    // Khi nhân vector ban đầu với ma trận M^(n-1), ta sẽ得到 vector trạng thái sau n bước.
    // Kết quả cần là số cách có tổng chia hết cho 7, tức là phần tử tại index 0.

    long long result = 0;
    // Vector ban đầu: index 6 là 1, các index khác là 0
    // result = sum(Mn[0][j] * initial[j])
    // initial[j] = 1 nếu j=6, else 0.
    // => result = Mn[0][6]
    result = Mn[0][6];

    cout << result << endl;
    return 0;
}
  • Time Complexity: O(log n)
  • Space Complexity: O(1)

Gọi dp[i][r] là số lượng dãy có độ dài i có tổng chia hết cho 7 dư r. Ta có công thức truy hồi: dp[i][(r + x) % 7] += dp[i-1][r] với mọi x ∈ {1,...,7}. Đây là một bài toán quy hoạch động tuyến tính có thể tăng quy mô bằng ma trận chuyển trạng thái. Ma trận A có kích thước 7x7, trong đó A[i][j] là số lượng số x ∈ {1,...,7} sao cho (i + x) % 7 = j. Vì với mỗi i, có đúng 1 số x cho mỗi giá trị j (x = (j - i + 7) % 7), nên ma trận A toàn 1. Số lượng dãy độ dài n có tổng chia hết cho 7 là phần tử tại (0, 6) của ma trận A^(n-1). Ta tính A^(n-1) bằng lũy thừa ma trận nhanh với độ phức tạp O(log n).

Cách Tính quy luật (Công thức trực tiếp)
#include <iostream>

long long power(long long base, long long exp) {
    long long res = 1;
    long long mod = 1e9 + 7;
    base %= mod;
    while (exp > 0) {
        if (exp % 2 == 1) {
            res = (res * base) % mod;
        }
        base = (base * base) % mod;
        exp /= 2;
    }
    return res;
}

int main() {
    long long n;
    std::cin >> n;
    if (n == 0) {
        std::cout << 0 << std::endl;
        return 0;
    }
    // Với n >= 1, kết quả là 7^(n-1) % (10^9 + 7)
    long long result = power(7, n - 1);
    std::cout << result << std::endl;
    return 0;
}
  • Time Complexity: O(log n)
  • Space Complexity: O(1)

Đây là giải pháp tối ưu nhất được phân tích từ các submitted solutions. Ta có thể thấy một quy luật: với n=1, có đúng 1 dãy thỏa mãn (dãy [7]). Với n=2, có 7 dãy (các dãy [x, 7-x] với x từ 1 đến 7). Về tổng quát, số lượng dãy thỏa mãn cho độ dài n là 7^(n-1). Giải thích: Tổng của dãy a1, ..., an chia hết cho 7 tức là (a1 + ... + a{n-1} + an) ≡ 0 (mod 7). Nếu ta chọn tự do các giá trị a1, ..., a{n-1} (mỗi phần tử có 7 lựa chọn), thì tổng của chúng là S'. Giá trị an cần chọn phải thỏa mãn an ≡ -S' (mod 7). Vì an ∈ {1,...,7}, và 7 ≡ 0 (mod 7), nên các giá trị an tương ứng với các dư từ 1 đến 6 đều có 1 lựa chọn duy nhất (chính là giá trị của dư đó), và an = 7 tương ứng với dư 0. Như vậy, với mỗi bộ (a1, ..., a{n-1}), luôn có đúng 1 giá trị a_n trong khoảng [1,7] sao cho tổng chia hết cho 7. Do đó, số lượng dãy thỏa mãn bằng số lượng cách chọn tự do n-1 phần tử đầu tiên. Số cách = 7 * 7 * ... * 7 (n-1 lần) = 7^(n-1).

Phân tích độ phức tạp

Cách tiếp cận Time Space Tên
1 O(log n) O(1) Quy hoạch động kết hợp Ma trận (Matrix Exponentiation)
2 O(log n) O(1) Tính quy luật (Công thức trực tiếp)

Bài học kinh nghiệm

  • Bài toán có thể biến đổi thành bài toán quy hoạch động với ma trận chuyển trạng thái 7x7.
  • Có một quy luật trực tiếp: với n phần tử, số lượng dãy thỏa mãn là 7^(n-1).
  • Phần tử cuối cùng được xác định duy nhất bởi tổng của n-1 phần tử đầu tiên.

Lỗi thường gặp

  • Xử lý sai trường hợp n=0 (nếu input có thể là 0).
  • Lũy thừa ma trận hoặc lũy thừa số cần xử lý tràn số (modulo 10^9+7) đúng cách.
  • Nhầm lẫn giữa số lượng dãy có tổng chia hết cho 7 và tổng số dãy có thể tạo ra (7^n).

Bình luận

Please read the guidelines before commenting.


Không có bình luận tại thời điểm này.