\(\Huge \text{🌱 Tin học trẻ 2021 - Vòng Khu vực - Bảng C - Thi đấu cầu lông}\)
Greedy
, Data Structure
, Sorting
Với mỗi truy vấn, ta dựng đồ thị hai phía:
Kết quả bài toán là số cặp ghép tối đa có thể tạo được trên đồ thị hai phía kể trên
Để tìm số cặp ghép cực đại trên đồ thị hai phía ta có thể có thể dùng thuật toán Kuhn-Munkres hoặc Hopcroft-Karp-Karzanov
Tất nhiên ta có thể chuyển đồ thị hai phía về dạng luồng và sử dụng thuật toán Ford-Fulkerson, Edmonds-Karp, Dinic, Push-relabel
Với mỗi truy vấn, ta giải như sau:
Không mất tính tổng quát ta sắp xếp các các vận động viên tăng dần
Gọi tập ứng cử viên \(S(u)\) là tập vào thời điểm \(u\) có các giá trị \(y_v\) mà tồn tại cặp \((x_v, y_v)\) thỏa mãn \((x_v < a_u)\), và chỉ những đỉnh \(v\) này thì \(u\) mới bắt cặp được
Từ định nghĩa của \(S(u)\) thì với mọi \(u \leq u'\) ta có \(S(u) \subseteq S(u')\)
Từ hai tính ứng dụng trên, nhận xét rằng khi có tập \(T\) là các cạnh \(u-v\) được bắt cặp, nếu ta luôn bắt cặp giá trị \(y_v\) lớn nhất chưa được chọn cho mỗi \(u\) thì luôn đảm bảo là cho ra kết quả không tệ hơn mọi cách bắt cặp khác
Vậy, thuật toán tham lam của ta như sau:
Để thêm bớt và tìm kiếm đủ nhanh cho một tập các giá trị có thể trùng nhau \(S(u)\), ta có thể sài các cấu trúc dữ liệu dạng cây:
C++
có std::multiset
còn không bạn có thể dùng trie vì có hằng số nhỏ và code dễstd::map
, C++ ordered_set
có vẻ như không đủ nhanh để qua time limit
Độ phức tạp:
General Time: \(O(q \times n \log n)\)
Query Time: \(O(n \log n)\)
Space: \(O(n)\)
Algo: Greedy, Data Structure, Sorting
#include <algorithm>
#include <iostream>
#include <set>
using namespace std;
const int LIM = 1e5 + 15;
int NumEarth, NumGamer, NumAlien, NumQuery;
pair<int, int> Earth[LIM]; /// Earth player
pair<int, int> Gamer[LIM]; /// All Z planet players
pair<int, int> Alien[LIM]; /// Few Z planet players that in each query
void input()
{
cin >> NumEarth >> NumGamer >> NumQuery;
for (int idx = 1; idx <= NumEarth; ++idx) cin >> Earth[idx].first;
for (int idx = 1; idx <= NumEarth; ++idx) cin >> Earth[idx].second;
for (int idx = 1; idx <= NumGamer; ++idx) cin >> Gamer[idx].first;
for (int idx = 1; idx <= NumGamer; ++idx) cin >> Gamer[idx].second;
sort(Earth + 1, Earth + NumEarth + 1); /// Sort these players
}
int query()
{
int sft;
cin >> sft;
/// We find the set of player to play in this query
NumAlien = NumEarth;
for (int idx = 1; idx <= NumAlien; ++idx) Alien[idx] = Gamer[idx + sft - 1];
/// Let also sort it
sort(Alien + 1, Alien + NumAlien + 1);
int result = 0;
multiset<int> Candidate; /// Out candidate list S(u)
for (int idx = 1, pos = 1; idx <= NumEarth; ++idx) /// For each u
{
/// Such $y_v$ satisfied exist such pair $(x_v, y_v)$ that $x_v < a_u$
/// We only take such $y_v$ that not belong to $S(u - 1)$
for (; pos <= NumAlien && Alien[pos].first < Earth[idx].first; ++pos)
Candidate.insert(Alien[pos].second); /// Add $y_v$ to $S(u)$
/// If there exist at least one solution
if (Candidate.size() && *Candidate.begin() < Earth[idx].second)
{
/// Erase this $y_v$ out of $S(u)$
Candidate.erase(--Candidate.lower_bound(Earth[idx].second));
/// Increase the result
++result;
}
}
return result;
}
int main()
{
// file("Test");
ios::sync_with_stdio(NULL);
cin.tie(NULL);
input();
while (NumQuery-->0) cout << query() << "\n";
return 0;
}
Bạn có thể giải truy vấn nhanh hơn \(O(n)\) khi \(q\) lớn hơn và \(n\) đủ nhỏ không ?