Giải thuật và Mã nguồn Các Bài Toán ABC452

A. Xác định Ngày Đặc Biệt

Bài toán yêu cầu kiểm tra xem cặp số nguyên (a, b) có trùng với một trong các ngày cố định: (1,7), (3,3), (5,5), (7,7), hay (9,9). Nếu khớp, in ra "Yes", ngược lại in "No".
#include <iostream>
#include <vector>
using namespace std;

int main() {
    int day, month;
    cin >> day >> month;

    vector<pair<int, int>> special = {{1,7}, {3,3}, {5,5}, {7,7}, {9,9}};
    bool found = false;
    for (auto& p : special) {
        if (p.first == day && p.second == month) {
            found = true;
            break;
        }
    }

    cout << (found ? "Yes" : "No") << endl;
    return 0;
}

B. Vẽ Khung Hình Chữ Nhật

Cho kích thước chiều cao a và chiều rộng b, cần in ra một khung hình chữ nhật bằng ký tự '#', bên trong là khoảng trắng '.'. Hàng đầu và hàng cuối gồm toàn '#'; các hàng giữa bắt đầu và kết thúc bằng '#', phần giữa là '.'.
#include <iostream>
using namespace std;

int main() {
    int height, width;
    cin >> height >> width;

    // Hàng trên
    cout << string(width, '#') << '\n';

    // Các hàng giữa
    for (int i = 1; i < height - 1; ++i) {
        cout << '#' << string(width - 2, '.') << '#';
        if (i < height - 2) cout << '\n';
    }

    // Hàng dưới (nếu có hơn 1 hàng)
    if (height > 1) {
        cout << '\n' << string(width, '#');
    }

    return 0;
}

C. Kiểm Tra Chuỗi Trung Tâm

Cho danh sách m chuỗi và n ràng buộc dạng (độ dài mong muốn, vị trí ký tự, ký tự mong đợi). Với mỗi chuỗi s_i có độ dài đúng bằng n, ta kiểm tra liệu với mọi ràng buộc thứ j, tồn tại ít nhất một chuỗi khác trong danh sách có độ dài bằng a_j và ký tự tại vị trí b_j bằng s_i[b_j]. Sử dụng mảng đếm ba chiều cnt[len][pos][ch] để ghi nhận tần suất.
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main() {
    int n; cin >> n;
    vector<pair<int, int>> constraints(n);
    for (int i = 0; i < n; ++i) {
        cin >> constraints[i].first >> constraints[i].second;
        constraints[i].second--; // chuyển sang chỉ số 0-based
    }

    int m; cin >> m;
    vector<string> words(m);
    // Đếm: cnt[length][position][char_index]
    int cnt[15][15][26] = {};

    for (int i = 0; i < m; ++i) {
        cin >> words[i];
        int len = words[i].size();
        if (len < 15) {
            for (int j = 0; j < len; ++j) {
                char c = words[i][j];
                if (c >= 'a' && c <= 'z') {
                    cnt[len][j][c - 'a']++;
                }
            }
        }
    }

    for (const string& s : words) {
        if (s.length() != (size_t)n) {
            cout << "No\n";
            continue;
        }

        bool valid = true;
        for (int j = 0; j < n; ++j) {
            int req_len = constraints[j].first;
            int req_pos = constraints[j].second;
            char req_char = s[j];
            if (req_pos < 0 || req_pos >= 15 || req_len >= 15 ||
                req_char < 'a' || req_char > 'z' ||
                cnt[req_len][req_pos][req_char - 'a'] == 0) {
                valid = false;
                break;
            }
        }
        cout << (valid ? "Yes" : "No") << '\n';
    }

    return 0;
}

D. Đếm Đoạn Không Chứa Chuỗi Con

Với hai chuỗi ST, đếm số đoạn con liên tiếp S[l..r] sao cho T không xuất hiện dưới dạng chuỗi con (không nhất thiết liên tục) trong đoạn đó. Giải pháp: với mỗi vị trí bắt đầu l, tìm vị trí nhỏ nhất pos sao cho S[l..pos] chứa T như một chuỗi con — sử dụng bảng next_pos[i][c] để truy vấn nhanh ký tự tiếp theo.
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

int main() {
    string S, T;
    cin >> S >> T;
    int lenS = S.size(), lenT = T.size();

    // Bảng next: next_pos[i][c] = vị trí nhỏ nhất >= i của ký tự c trong S
    vector<vector<int>> next_pos(lenS + 1, vector<int>(26, lenS));
    for (int i = lenS - 1; i >= 0; --i) {
        for (int c = 0; c < 26; ++c) {
            next_pos[i][c] = next_pos[i + 1][c];
        }
        next_pos[i][S[i] - 'a'] = i;
    }

    long long result = 0;
    for (int l = 0; l < lenS; ++l) {
        int ptr = l;
        bool complete = true;
        for (char c : T) {
            int idx = c - 'a';
            if (ptr >= lenS || next_pos[ptr][idx] == lenS) {
                complete = false;
                break;
            }
            ptr = next_pos[ptr][idx] + 1;
        }
        if (!complete) {
            result += lenS - l;
        } else {
            result += ptr - l - 1;
        }
    }

    cout << result << '\n';
    return 0;
}

E. Tổng Có Điều Kiện Trên Phép Chia Nguyên

Tính tổng: \[ \sum_{i=1}^{N}\sum_{j=1}^{M} A_i B_j \cdot (i \bmod j) \] Chuyển đổi thành: \[ \left(\sum_i i A_i\right)\left(\sum_j B_j\right) - \sum_j j B_j \left(\sum_i A_i \left\lfloor \frac{i}{j} \right\rfloor\right) \] Phần thứ hai được tính bằng cách chia miền giá trị của \(\left\lfloor i/j \right\rfloor\) thành các đoạn \([k \cdot j,\; (k+1) \cdot j - 1]\), rồi dùng mảng tổng tiền tố để lấy tổng \(A_i\) trên từng đoạn.
#include <iostream>
#include <vector>
using namespace std;
const long long MOD = 998244353;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int N, M;
    cin >> N >> M;

    vector<long long> A(N + 1), B(M + 1), prefix(N + 1);
    long long sumB = 0;

    for (int i = 1; i <= N; ++i) {
        cin >> A[i];
        prefix[i] = (prefix[i - 1] + A[i]) % MOD;
    }
    for (int j = 1; j <= M; ++j) {
        cin >> B[j];
        sumB = (sumB + B[j]) % MOD;
    }

    long long ans = 0;

    // Phần đầu: sum(i * A_i) * sum(B_j)
    for (int i = 1; i <= N; ++i) {
        ans = (ans + i % MOD * A[i] % MOD * sumB) % MOD;
    }

    // Phần sau: sum_j [ j * B_j * sum_i A_i * floor(i/j) ]
    for (int j = 1; j <= M; ++j) {
        long long inner = 0;
        for (int k = 0; k * j <= N; ++k) {
            int left = max(1, k * j);
            int right = min(N, (k + 1) * j - 1);
            if (left <= right) {
                long long seg_sum = (prefix[right] - prefix[left - 1] + MOD) % MOD;
                inner = (inner + k % MOD * seg_sum) % MOD;
            }
        }
        ans = (ans - j % MOD * B[j] % MOD * inner % MOD + MOD) % MOD;
    }

    cout << ans << '\n';
    return 0;
}

F. Đếm Đoạn Con Với Số Nghịch Thế Cố Định

Đếm số đoạn con [l, r] của dãy A sao cho số nghịch thế trong đoạn đúng bằng k. Dùng kỹ thuật "hai con trỏ" kết hợp cây Fenwick (Binary Indexed Tree) để duy trì số nghịch thế khi mở rộng hoặc thu hẹp đoạn. Hàm count_at_most(k) trả về số đoạn có tối đa k nghịch thế; kết quả cuối là count_at_most(k) - count_at_most(k-1).
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

struct Fenwick {
    vector<long long> tree;
    int n;
    Fenwick(int size) : n(size), tree(size + 1) {}
    void update(int i, long long delta) {
        for (++i; i <= n; i += i & -i) tree[i] += delta;
    }
    long long query(int i) {
        long long res = 0;
        for (++i; i > 0; i -= i & -i) res += tree[i];
        return res;
    }
    long long range_query(int l, int r) {
        return query(r) - query(l - 1);
    }
};

long long count_at_most(const vector<int>& A, int k) {
    int n = A.size();
    vector<int> sorted = A;
    sort(sorted.begin(), sorted.end());
    sorted.erase(unique(sorted.begin(), sorted.end()), sorted.end());
    int sz = sorted.size();

    auto get_id = [&](int x) {
        return lower_bound(sorted.begin(), sorted.end(), x) - sorted.begin();
    };

    Fenwick bit(sz);
    long long inv = 0;
    long long ans = 0;
    int left = 0;

    for (int right = 0; right < n; ++right) {
        int id = get_id(A[right]);
        inv += bit.range_query(id + 1, sz - 1);
        bit.update(id, 1);

        while (inv > k && left <= right) {
            int lid = get_id(A[left]);
            bit.update(lid, -1);
            inv -= bit.range_query(0, lid - 1);
            ++left;
        }
        ans += right - left + 1;
    }
    return ans;
}

int main() {
    int n, k;
    cin >> n >> k;
    vector<int> A(n);
    for (int& x : A) cin >> x;

    cout << count_at_most(A, k) - count_at_most(A, k - 1) << '\n';
    return 0;
}

Thẻ: atcoder competitive-programming cpp fenwick-tree string-algorithms

Đăng vào ngày 31 tháng 5 lúc 11:48