UVa 11020 Efficient Solutions
题目描述
CentauriPrime\texttt{Centauri Prime}Centauri Prime 的公主是今年银河系最抢手的单身女性。许多求婚者在皇宫前排起长队,希望能有 555 分钟的时间来打动她。每位绅士在 555 分钟后会被皇宫守卫带出皇家房间,公主会根据他的家世和魅力给出两个分数。在 CentauriPrime\texttt{Centauri Prime}Centauri Prime,分数越低越好。
对于两位绅士 AAA 和 BBB,公主分别给出分数 (LA,CA)(L_A, C_A)(LA,CA) 和 (LB,CB)(L_B, C_B)(LB,CB)。如果满足以下条件之一,则称 BBB 支配 AAA:
- LB<LAL_B < L_ALB<LA 且 CB≤CAC_B \leq C_ACB≤CA,或者
- LB≤LAL_B \leq L_ALB≤LA 且 CB<CAC_B < C_ACB<CA
换句话说,如果 BBB 在至少一个维度上严格优于 AAA,且在另一个维度上不差于 AAA,则 BBB 支配 AAA。
公主维护着一个有效求婚者列表,其中包含所有目前未被任何已见到的绅士支配的绅士。每次新的 555 分钟会面后,她都会更新这个列表。
给定求婚者队列和公主给他们的分数,确定每次更新后有效列表的大小。
输入格式
- 第一行:测试用例数量 NNN (0<N<400 < N < 400<N<40)
- 每个测试用例:
- 第一行:队列大小 nnn (0≤n≤150000 \leq n \leq 150000≤n≤15000)
- 接下来 nnn 行:每行两个整数(范围 [0,109][0, 10^9][0,109]),表示家世和魅力分数
输出格式
对于每个测试用例:
- 输出一行
Case #z:
- 接下来 nnn 行,第 iii 行包含第 iii 次更新后有效列表的大小
- 测试用例之间输出一个空行
题目分析
问题本质
这是一个典型的 Pareto\texttt{Pareto}Pareto 最优维护问题。我们需要动态维护一个二维点的集合,使得集合中任意两点都不存在支配关系。
支配关系理解
支配关系的定义可以简化为:点 BBB 支配点 AAA 当且仅当:
- BBB 在两个维度上都不比 AAA 差,且
- 至少在一个维度上严格优于 AAA
数学表达为:
(LB≤LA且 CB≤CA)且 (LB<LA或 CB<CA)(L_B \leq L_A \ \text{且} \ C_B \leq C_A) \ \text{且} \ (L_B < L_A \ \text{或} \ C_B < C_A)(LB≤LA 且 CB≤CA) 且 (LB<LA 或 CB<CA)
算法设计思路
-
数据结构选择:使用
multiset
存储有效点,按家世分数升序排序,家世相同时按魅力分数升序排序。 -
新点处理流程:
- 检查是否被支配:对于新点 (L,C)(L, C)(L,C)
- 检查前一个点(家世 ≤L\leq L≤L)是否支配新点
- 检查同家世的点是否支配新点
- 如果未被支配:
- 插入新点
- 删除所有被新点支配的点
- 检查是否被支配:对于新点 (L,C)(L, C)(L,C)
-
复杂度分析:
- 每个点最多被插入一次、删除一次
multiset
操作时间复杂度为 O(logn)O(\log n)O(logn)- 总体复杂度 O(nlogn)O(n \log n)O(nlogn),对于 n≤15000n \leq 15000n≤15000 是可接受的
关键难点
- 重复点处理:必须使用
multiset
而非set
,因为可能存在多个相同的点 - 支配检查的完整性:需要同时检查家世较小和家世相同的点
- 高效删除:利用集合的有序性,只检查可能被支配的点
算法实现细节
支配检查
对于新点 (L,C)(L, C)(L,C):
- 找到第一个家世 ≥L\geq L≥L 的点
it
- 检查
prev(it)
是否支配新点(家世 ≤L\leq L≤L 且魅力 ≤C\leq C≤C,且至少一个严格小于) - 检查同家世的点是否支配新点(魅力严格小于)
删除被支配点
插入新点后:
- 从新点的下一个位置开始遍历
- 删除所有满足家世 ≥L\geq L≥L 且魅力 ≥C\geq C≥C,且至少一个严格大于的点
参考代码
// Efficient Solutions
// UVa ID: 11020
// Verdict: Accepted
// Submission Date: 2025-10-21
// UVa Run Time: 0.820s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net#include <bits/stdc++.h>using namespace std;int main() {ios_base::sync_with_stdio(false);cin.tie(NULL);int N;cin >> N;for (int caseNum = 1; caseNum <= N; caseNum++) {int n;cin >> n;multiset<pair<int, int>> efficient; // 存储有效点,按家世升序,家世相同时按魅力升序vector<int> result; // 存储每次更新后的结果for (int i = 0; i < n; i++) {int L, C;cin >> L >> C;pair<int, int> p = {L, C};// 检查新点是否被支配auto it = efficient.lower_bound({L, INT_MIN}); // 找到第一个家世>=L的点bool dominated = false;// 检查前一个点(家世<=L)是否支配新点if (it != efficient.begin()) {auto prev = it;--prev;if (prev->first <= L && prev->second <= C) { // 前一个点两个维度都不比新点差if (prev->first < L || prev->second < C) { // 且至少一个严格优于dominated = true;}}}// 检查同家世的点是否支配新点if (!dominated) {while (it != efficient.end() && it->first == L) {if (it->second < C) { // 同家世但魅力更优dominated = true;break;}++it;}}// 如果被支配,跳过插入if (dominated) {result.push_back(efficient.size());continue;}// 插入新点efficient.insert(p);// 删除所有被新点支配的点it = efficient.upper_bound(p); // 从新点的下一个开始while (it != efficient.end()) {if (it->first >= L && it->second >= C) { // 当前点两个维度都不比新点差if (it->first > L || it->second > C) { // 且新点至少一个严格优于it = efficient.erase(it); // 删除被支配的点} else {++it; // 完全相同,保留}} else {++it; // 不被支配,继续检查}}result.push_back(efficient.size());}// 输出结果cout << "Case #" << caseNum << ":\n";for (int sz : result) {cout << sz << "\n";}if (caseNum < N) {cout << "\n";}}return 0;
}
总结
本题的关键在于正确理解支配关系并高效维护 Pareto\texttt{Pareto}Pareto 最优集合。通过使用 multiset
和精心设计的检查逻辑,我们可以在 O(nlogn)O(n \log n)O(nlogn) 时间内解决问题。算法充分利用了平衡二叉搜索树的有序特性,确保了高效的点插入、查找和删除操作。