# Orange lecture #12 - Beautiful People # Đề bài Có N người, mỗi người có sức mạnh S[i] và độ đẹp B[i]. Hai người i và j ghét nhau nếu S[i] <= S[j] và B[i] >= B[j], hoặc S[i] >= S[j] và B[i] <= B[j]. Ngược lại nếu cả 2 thuộc tính của một người lớn hơn 2 thuộc tính tương ứng của người kia thì cả 2 không ghét nhau (S[i] < S[j] & B[i] < B[j] hoặc S[i] > S[j] & B[i] > B[j]). Chọn nhiều người tham gia bữa tiệc nhất sao cho không có 2 người nào được chọn ghét nhau. ### Input - $N (2 \leq N \leq 10^5)$ - số người - $N$ dòng tiếp theo, mỗi dòng chứa 2 số nguyên $S[i]$ $B[i]$ $(1 \leq S[i], B[i] \leq 10^9)$ ### Output - M - số người được chọn - M số nguyên trên một dòng - chỉ số của những người được chọn **Ví dụ** ``` +-----------------+ |4 | |1 1 | |1 2 | |2 1 | |2 2 | +-----------------+ |2 | |1 4 | +-----------------+ ``` ### **Giải thích ví dụ** - Chọn người thứ nhất & người thứ 4 ### **Hướng dẫn giải** - Đưa về bài toán LIS: Sắp xếp S tăng dần, nếu cùng S thì sắp xếp B giảm dần VD: (1, 8) (1, 6) (1, 4) (1, 3) (2, 5), (2, 5) (2, 4) (3, 7) (3, 6) - Lúc này việc tìm "dãy con tăng" (theo cả S và B) dài nhất sẽ quy về việc tìm dãy con tăng dài nhất theo B: + Các phần tử có cùng S không bị ghép với nhau + VD (1, 8) không thể bị ghép vào sau (1, 3) vì (1, 3) chưa xuất hiện + Các phần tử có cùng S và cùng B không bị ghép với nhau + VD (2, 5) không thể bị ghép vào sau (2, 5) vì B phải tăng dần ```python # input ... people = [] for i = 0 -> n - 1: people.append((S[i], -B[i], i)) people.sort() # dp = [1] * n # dp[i] = LIS | the last element is people[i] for i = 1 -> n - 1: for j = 0 -> i - 1: if(B[j] < B[i]): dp[i] = max(dp[i], dp[j] + 1) print(max(dp[])) ``` **Cải tiến với Binary search** ```python # input ... people = [] for i = 0 -> n - 1: people.append((S[i], -B[i], i + 1)) people.sort() # dp = [] # dp[i] = index of the smallest possible last element of an increasing subsequence of length i + 1 int findPosition(int i): l = 0; r = len(dp) - 1; pos = len(dp) # find the first position in dp whose >= people[i].B while(l <= r): mid = (l + r) / 2 # B is negative so we need to reverse it before compare if(-people[dp[mid]][1] >= -people[i][1]): pos = mid r = mid - 1 else: l = mid + 1 return pos # dp LIS for i = 0 -> n - 1: pos = findPosition(i) if(pos >= len(dp)): dp.append(i) else: dp[pos] = i print(len(dp)) ``` **Truy vết** ```python # input ... people = [] for i = 0 -> n - 1: people.append((S[i], -B[i], i + 1)) people.sort() # dp = [] # dp[i] = index of the smallest possible last element of an increasing subsequence of length i + 1 previous = [-1] * n int findPosition(int i): l = 0; r = len(dp) - 1; pos = len(dp) # find the first position in dp whose >= people[i].B while(l <= r): mid = (l + r) / 2 # B is negative so we need to reverse it before compare if(-people[dp[mid]][1] >= -people[i][1]): pos = mid r = mid - 1 else: l = mid + 1 return pos # dp LIS for i = 0 -> n - 1: pos = findPosition(i) if(pos >= len(dp)): dp.append(i) else: dp[pos] = i previous[i] = pos <= 0 ? -1 : dp[pos - 1] # print result result = len(dp) invitedPeople = [] cur = dp[-1] while(cur >= 0): invitedPeople.append(people[cur].index) cur = previous[cur] reverse(invitedPeople) for p in invitedPeople: print(p,' ') ``` [Code C++](https://ideone.com/Gv0JN0) **Độ phức tạp:** $O(NlogN)$