Hướng dẫn giải của Kinh doanh (Đề chọn ĐTQG Phú Thọ 2014-2015)


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.

Hướng dẫn giải

Cuội chỉ mở một con đường nối hai làng bất kỳ. Nhà máy đặt ở một làng nào đó, và các làng có đường đi đến nhà máy là toàn bộ thành phần liên thông chứa nhà máy. Vậy đáp án là kích thước lớn nhất của một thành phần sau khi thêm đúng một cạnh.

Sub 1: Thử mọi cạnh mới — ~O(n^2 \cdot (n + m))~, dùng khi ~n \le 50~

Với mọi cặp làng (a, b), thêm cạnh (a, b) rồi tìm thành phần lớn nhất bằng DSU. Lấy giá trị lớn nhất. Nếu chỉ nộp sub này: đúng 16/20 test.

Sub 2: DSU và hai thành phần lớn nhất — ~O((n + m) \cdot \alpha(n))~

Tìm kích thước các thành phần liên thông. Nếu chỉ có một thành phần thì đáp án là n; nếu có nhiều hơn thì đáp án là tổng hai kích thước lớn nhất, vì nối hai thành phần này bằng một cạnh là tối ưu. Đúng 20/20 test.

Chương trình hoàn chỉnh

Hàm main đọc n, m và các cạnh. Nếu n \le 50 gọi sub 1, còn lại gọi sub 2.

#include <bits/stdc++.h>
using namespace std;

int n, m;
vector<pair<int, int>> E;

// Đáp án: Cuội mở đúng một đường nối hai làng. Nhà máy đặt ở một làng bất kỳ,
// các làng có đường đi đến nhà máy là toàn bộ thành phần liên thông chứa nhà máy.
// Vậy đáp án = kích thước lớn nhất của một thành phần sau khi thêm một cạnh.

// Sub 1: vét cạn O(n^2 * (n + m)) — thử mọi cạnh mới (a, b), tính thành phần liên thông lớn nhất
// bằng DSU. Dùng khi n <= 50.
int par[10005];
int findp(int x) { return par[x] == x ? x : par[x] = findp(par[x]); }

int largestComp(int extraA, int extraB) {
    for (int i = 1; i <= n; i++) par[i] = i;
    auto unite = [&](int a, int b) {
        a = findp(a);
        b = findp(b);
        if (a != b) par[a] = b;
    };
    for (auto &e : E) unite(e.first, e.second);
    if (extraA != 0) unite(extraA, extraB);
    vector<int> sz(n + 1, 0);
    int best = 0;
    for (int i = 1; i <= n; i++) best = max(best, ++sz[findp(i)]);
    return best;
}

void sub1() {
    if (n == 1) {
        cout << 1 << "\n";
        return;
    }
    int best = 0;
    for (int a = 1; a <= n; a++)
        for (int b = a + 1; b <= n; b++)
            best = max(best, largestComp(a, b));
    cout << best << "\n";
}

// Sub 2: DSU tính kích thước các thành phần. Nếu có từ hai thành phần trở lên thì nối hai thành phần
// lớn nhất, đáp án là tổng hai kích thước lớn nhất. Nếu chỉ có một thành phần thì đáp án là n.
void sub2() {
    for (int i = 1; i <= n; i++) par[i] = i;
    for (auto &e : E) {
        int a = findp(e.first), b = findp(e.second);
        if (a != b) par[a] = b;
    }
    vector<int> sz(n + 1, 0);
    for (int i = 1; i <= n; i++) sz[findp(i)]++;
    vector<int> comps;
    for (int i = 1; i <= n; i++)
        if (sz[i] > 0) comps.push_back(sz[i]);
    sort(comps.rbegin(), comps.rend());
    if (comps.size() == 1) cout << comps[0] << "\n";
    else cout << comps[0] + comps[1] << "\n";
}

int main() {
    freopen("CAU3.INP", "r", stdin);
    freopen("CAU3.OUT", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> m;
    E.resize(m);
    for (int i = 0; i < m; i++) cin >> E[i].first >> E[i].second;
    if (n <= 50) sub1();
    else sub2();
    return 0;
}

Bình luận

Hãy đọc nội quy trước khi bình luận.


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