Editorial for Bedao OI Contest 1 - Bất phương trình tuyến tính


Remember to use this editorial only when stuck, and not to copy-paste code from it. Please be respectful to the problem author and editorialist.
Submitting an official solution before solving the problem yourself is a bannable offence.

Author: Lam_lai_cuoc_doi

Subtask 1: ~\lfloor \frac{m}{a_1} \rfloor~

Subtask 2: Xét bất phương trình ~ax + by \leq m~; số nghiệm nguyên không âm của bất phương trình là:

~\sum_{x=0}^{\lfloor \frac{m}{a} \rfloor}(\lfloor \frac{m - x \times a}{b} \rfloor + 1)~

Vì ~b \leq 10~ nên trong 10 số ~a \times 1~, ~a \times 2~, ~\ldots~, ~a \times 10~ có ít nhất hai số đồng dư với nhau theo modulo ~b~

Với mỗi số dư ~0~, ~1~, ~2~, ~\ldots~, ~b - 1~; ta xem nó đóng góp bao nhiêu cho đáp án:

Nếu cố định số dư ~r~, ta tìm được số ~m'~ lớn nhất mà ~m' \leq m~ và ~m' \equiv r \pmod{b}~; số lượng đóng góp của các ~x \times a \equiv r \pmod{b}~ là ~\frac{m - x \times a}{b} + 1~. Ta tách riêng ~m'~ và ~x \times a~ và dùng công thức cho từng phần

Subtask 3: Bài toán sẽ tương đương với đếm số nghiệm không âm của bất phương trình: ~a_1 \times x_1 + a_2 \times x_2 + \ldots + a_n \times x_n~ ~\leq m - a_1 - a_2 - \ldots - a_n~

Lúc này bài toán chính là bài toán CSES - Coin Combinations II

Subtask 4: Sử dụng dp digit, ta xây dựng từng đơn vị cho tất cả các biến như code bên dưới

Code mẫu

#include <iostream>
#include <cstdio>
#include <algorithm>
#define task ""
using namespace std;
using ll = long long;
using ld = long double;

const int N = 5e2 + 5;
const ll mod = 1e9 + 7;
int a[N];
string n;
int m, k;
ll dp[N][2][12][2005];

void Read()
{
    cin >> k >> m;

    for (int i = 1; i <= k; ++i) {
        cin >> a[i];
        m -= a[i];
    }

    if(m < 0) {
        cout << 0;
        exit(0);
    }

    n = to_string(m);
    reverse(n.begin(), n.end());
    m = n.size() - 1;
}

void Solve()
{
    dp[0][0][0][0] = 1;
    for (int i = 0; i <= m; ++i)
    {
        for (int j = 1; j <= k; ++j)
            for (int t = 0; t < 2000; ++t)
                for (int h = 0; h < 10; ++h)
                    if (h * a[j] <= t)
                    {
                        dp[i][0][j][t] = (dp[i][0][j][t] + dp[i][0][j - 1][t - a[j] * h]) % mod;
                        dp[i][1][j][t] = (dp[i][1][j][t] + dp[i][1][j - 1][t - a[j] * h]) % mod;
                    }

        for (int j = 0; j < 2000; ++j)
            if (j % 10 < n[i] - '0')
            {
                dp[i + 1][0][0][j / 10] = (dp[i + 1][0][0][j / 10] + dp[i][0][k][j]) % mod;
                dp[i + 1][0][0][j / 10] = (dp[i + 1][0][0][j / 10] + dp[i][1][k][j]) % mod;
            }
            else if (j % 10 > n[i] - '0')
            {
                dp[i + 1][1][0][j / 10] = (dp[i + 1][1][0][j / 10] + dp[i][0][k][j]) % mod;
                dp[i + 1][1][0][j / 10] = (dp[i + 1][1][0][j / 10] + dp[i][1][k][j]) % mod;
            }
            else
            {
                dp[i + 1][0][0][j / 10] = (dp[i + 1][0][0][j / 10] + dp[i][0][k][j]) % mod;
                dp[i + 1][1][0][j / 10] = (dp[i + 1][1][0][j / 10] + dp[i][1][k][j]) % mod;
            }
    }
    cout << dp[m + 1][0][0][0] % mod;
}

int32_t main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    if (fopen(task ".INP", "r"))
        freopen(task ".INP", "r", stdin),
            freopen(task ".OUT", "w", stdout);
    Read();
    Solve();
}

Comments

Please read the guidelines before commenting.