题目链接:https://codeforces.com/contest/1027/problem/F
14.1 题意
有 n ( 1 ≤ n ≤ 1 0 6 ) n(1 \le n \le 10^6) n(1≤n≤106) 场考试,每场考试 i i i 可以选择在第 a i a_i ai 天或者第 b i b_i bi( 1 ≤ a i < b i ≤ 1 0 9 1 \le a_i < b_i \le 10^9 1≤ai<bi≤109) 天两天中的一天参加。
现在必须参加所有考试,问最早的日期,使得截止到该日期,所有考试均已参加。
14.2 解题过程
首先对所有的日期进行离散化,之后对于每一场考试,将其所对应的两天相连边。
然后我们会发现,形成了多个连通块。考虑每一个连通块,设其点数为 n i n_i ni,边数为 m i m_i mi,则有以下几种情况:
-
n i − 1 = m i n_i - 1 = m_i ni−1=mi,此时连通块为一棵树,每条边都能找到一个点进行匹配,会多出来一个点。此情况下该连通块的答案为点集中的次大值。
-
n i = m i n_i = m_i ni=mi,此时连通块为一基环树,每条边都能找到一个点进行匹配,没有多余的点。此情况下该连通块的答案为点集中的最大值。
-
其他情况下,总会有边无法成功匹配,因此遇到这种情况,会导致整个问题无解。
最终的答案为所有连通块答案的最大值。
连通块可以使用并查集维护,也可以通过 DFS 进行染色之后维护,前者时间复杂度稍高。
注意本题比较卡常,离散化时用 unordered_map
竟然会 T,改成 map
之后就过了!
时间复杂度: O ( n log n + n α ( n ) ) O(n \log n + n \alpha (n)) O(nlogn+nα(n))。
14.3 错误点
-
处理每个联通块的最大值和次大值,最好将该连通块中的所有点丢到一个
vector
之后再进行处理。之前通过枚举边的方式来处理,很容易造成重复计算。 -
如果感觉使用
unordered_map
被卡常,可以考虑换成map
再进行尝试。 -
并查集虽然简单,写的时候一定要仔细,很容易在某些地方写错。
14.4 代码
int n, father[maxn], rank_[maxn], sz[maxn], num[maxn];
int val[maxn][2];
void init() {
for (int i = 0; i <= 2 * n; i++) {
father[i] = i;
rank_[i] = 0;
sz[i] = 0;
val[i][0] = val[i][1] = 0;
}
}
int find(int x) {
return father[x] == x ? x : father[x] = find(father[x]);
}
void merge(int x, int y) {
x = find(x);
y = find(y);
if (x != y) {
if (rank_[x] > rank_[y]) {
father[y] = x;
} else {
father[x] = y;
if (rank_[x] == rank_[y]) rank_[y]++;
}
}
}
int number[maxn];
map<int, int> mp;
int a[maxn], b[maxn];
vector<int> ve[maxn];
int main()
{
scanf("%d", &n);
init();
int tot = 0;
for (int i = 1; i <= n; i++) {
scanf("%d%d", &a[i], &b[i]);
number[++tot] = a[i];
number[++tot] = b[i];
}
sort(number + 1, number + 1 + tot);
tot = unique(number + 1, number + 1 + tot) - number - 1;
for (int i = 1; i <= tot; i++) {
mp[number[i]] = i;
}
for (int i = 1; i <= n; i++) {
int x = mp[a[i]];
int y = mp[b[i]];
merge(x, y);
}
for (int i = 1; i <= tot; i++) {
ve[find(i)].pb(number[i]);
}
for (int i = 1; i <= n; i++) {
int x = mp[a[i]];
int y = mp[b[i]];
y = find(y);
sz[y]++;
}
for (int i = 1; i <= tot; i++) {
if (father[i] != i) continue;
for (auto y: ve[i]) {
if (y > val[i][0]) {
val[i][1] = val[i][0];
val[i][0] = y;
} else if (y > val[i][1]) {
val[i][1] = y;
}
}
}
int ans = 0;
for (int i = 1; i <= tot; i++) {
int y = find(i);
if (sz[y] > ve[y].size()) return 0 * puts("-1");
else if (sz[y] == ve[y].size()) ans = max(ans, val[y][0]);
else ans = max(ans, val[y][1]);
}
printf("%d\n", ans);
return 0;
}