AGC029F
题意
给 \(n\ -\ 1\) 个集合 \(S_1,\ S_2,\ ...\ S_{n-1}\),每个集合选两个数,连边,使得最后形成一棵树。
\(2\ \leq\ n\ \leq\ 10^5\)
做法1
考虑不合法,当且仅当存在一个集合 \(T\ =\ \{T_1,\ T_2,\ ...,\ T_k\}\) 使得 \(|S_{T_1}\ \cup\ S_{T_2}\ \cup\ S_{T_3}\ \cup\ ...\ \cup\ S_{T_k}|\ <\ k\)。否则合法。
用 Hall 定理判断,得到一组 \(n\ -\ 1\) 的匹配。令剩余的一个点为根,对其跑匈牙利算法,就能得到一棵树。由于交错路,所以可以证明合法。
代码
#include <bits/stdc++.h>
#ifdef __WIN32
#define LLFORMAT "I64"
#else
#define LLFORMAT "ll"
#endif
using namespace std;
struct edge {
int to, cap, rev;
edge() {}
edge(int to, int cap, int rev): to(to), cap(cap), rev(rev) {}
};
struct max_flow {
int n, S, T;
vector<vector<edge> > g;
max_flow() {}
max_flow(int N): n(N) { g.resize(N); }
max_flow(int N, int s, int t): n(N), S(s), T(t) { g.resize(N); }
inline void add_edge(int from, int to, int cap) {
g[from].push_back(edge(to, cap, g[to].size()));
g[to].push_back(edge(from, 0, g[from].size() - 1));
return;
}
inline int dinic() {
vector<int> dst(n), iter(n);
auto bfs = [&]() {
fill(dst.begin(), dst.end(), -1);
queue<int> q;
q.push(S);
dst[S] = 0;
while(!q.empty()) {
int u = q.front();
q.pop();
for (auto &e: g[u]) if(e.cap && !~dst[e.to]) dst[e.to] = dst[u] + 1, q.push(e.to);
}
return dst[T] != -1;
};
int ret = 0;
while(bfs()) {
fill(iter.begin(), iter.end(), 0);
function<int(int, int)> dfs = [&](int u, int f) {
if(u == T) return f;
int t = f;
for (int &i = iter[u]; i < g[u].size(); ++i) {
auto &e = g[u][i];
if(e.cap && dst[e.to] == dst[u] + 1) {
int F = dfs(e.to, min(t, e.cap));
e.cap -= F;
g[e.to][e.rev].cap += F;
t -= F;
if(!t) return f;
}
}
return f - t;
};
static const int oo = INT_MAX;
for (int f = dfs(S, oo); f; f = dfs(S, oo)) ret += f;
}
return ret;
}
};
int main() {
ios::sync_with_stdio(false);
int n;
cin >> n;
vector<vector<int> > apr(n + 1);
max_flow flow(n * 2, 0, 1);
for (int i = 1; i < n; ++i) {
int sz, foo = i + n;
cin >> sz;
flow.add_edge(foo, 1, 1);
flow.add_edge(0, i + 1, 1);
while(sz--) {
int u;
cin >> u;
flow.add_edge(u, foo, 1);
apr[u].push_back(foo);
}
}
if(flow.dinic() != n - 1) { cout << "-1\n"; return 0; }
vector<int> rem(n * 2, -1), con(n * 2, -1);
for (int i = 1; i < n; ++i) {
int u = i + n;
for (auto &e: flow.g[u]) if(e.cap && e.to >= 2 && e.to <= n) rem[u] = e.to;
if(!~rem[u]) { cout << "-1\n"; return 0; }
}
queue<int> q;
q.push(1);
while(!q.empty()) {
int u = q.front();
q.pop();
for (int i: apr[u]) if(!~con[i]) {
q.push(rem[i]);
con[i] = u;
}
}
for (int i = 1; i < n; ++i) {
int u = i + n;
if(!~rem[u] || !~con[u]) { cout << "-1\n"; return 0; }
}
for (int i = 1; i < n; ++i) {
int u = i + n;
cout << rem[u] << ' ' << con[u] << endl;
}
return 0;
}