[AGC029F] Construction of a tree 题解

题意:

给定 \(n-1\) 个点集(全集为 \(\{1,2,\ldots,n\}\)),从每个集合内选两个点连边,使得最后形成一棵树。输出方案。

\(n \le 10^5\)


思路:

AGC 全是神仙题。

考虑树的性质,每个点除了根节点外刚好有一个父节点。而每条边必然连接了一个父节点和一个子节点。

如果考虑点与点之间没啥帮助,考虑这个点与其到父节点的那条边。

不难发现这说明点与点集构成了一个匹配,因为每个点集对应一条边。

不妨求出最大匹配,如果不是 \(n-1\) 肯定不行。

否则,我们找到右边唯一没有被匹配的点 \(rt\),以它为起点搜索,搜到一个未经过的点集则连一条它到那个点集的匹配点的边。

这样只要最后所有点都被遍历到了就可行。

点击查看代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e5 + 5;
const int inf = 0x3f3f3f3f;

struct Edge {
	int to, val, rev;
	Edge (int _to = 0, int _val = 0, int _rev = 0) :
		to(_to), val(_val), rev(_rev) {}
};
vector<Edge> e[N];
void add(int u, int v, int w) {
	e[u].push_back(Edge(v, w, (int)e[v].size()));
	e[v].push_back(Edge(u, 0, (int)e[u].size() - 1));
}

int d[N] = {0};
bool bfs(int s, int t) {
	memset(d, inf, sizeof d);
	queue<int> q;
	q.push(s), d[s] = 0;
	while (!q.empty()) {
		int h = q.front();
		q.pop();
		for (auto i: e[h])
			if (i.val > 0 && d[i.to] > d[h] + 1)
				d[i.to] = d[h] + 1, q.push(i.to);
	}
	return d[t] != inf;
}

int cur[N] = {0};
int dfs(int x, int t, int f) {
	if (x == t)
		return f;
	for (int i = cur[x]; i < (int)e[x].size(); i = ++cur[x])
		if (e[x][i].val > 0 && d[e[x][i].to] == d[x] + 1) {
			int fl = dfs(e[x][i].to, t, min(f, e[x][i].val));
			if (fl > 0) {
				e[x][i].val -= fl;
				e[e[x][i].to][e[x][i].rev].val += fl;
				return fl;
			}
		}
	d[x] = -1;
	return 0;
}

int Dinic(int s, int t) {
	int ans = 0;
	while (bfs(s, t)) {
		memset(cur, 0, sizeof cur);
		int ad = 0;
		while ((ad = dfs(s, t, inf)) > 0)
			ans += ad;
	}
	return ans;
}

int n;

pair<int, int> ans[N];
bool vis[N] = {false};
int mch[N] = {0};

int main() {
	cin >> n;
	for (int i = 1, c, x; i < n; i++) {
		cin >> c;
		while (c--) {
			cin >> x;
			add(i, x + n - 1, 1);
		}
	}
	int S = 0, T = n * 2;
	for (int i = 1; i < n; i++)
		add(S, i, 1);
	for (int i = 1; i <= n; i++)
		add(i + n - 1, T, 1);
	if (Dinic(S, T) != n - 1) {
		cout << -1;
		return 0; 
	} 
	int rt = 0;
	for (int i = 1; i <= n; i++) 
		for (auto j: e[i + n - 1])
			if (j.to == T && j.val == 1)
				rt = i;
			else if (j.to != T && j.val == 1)
				mch[j.to] = i;
	queue<int> q;
	q.push(rt);
	int tot = 0;
	while (!q.empty()) {
		int h = q.front();
		q.pop();
		++tot;
		for (auto i: e[h + n - 1])
			if (i.to != T && i.val == 0 && !vis[i.to]) {//未匹配边 
				vis[i.to] = true;
				ans[i.to] = make_pair(h, mch[i.to]);
				q.push(mch[i.to]);
			}
	}
	if (tot < n) {
		cout << -1;
		return 0; 
	}
	for (int i = 1; i < n; i++)
		cout << ans[i].first << " " << ans[i].second << endl;
	return 0;
} 
posted @ 2024-06-01 11:53  rlc202204  阅读(9)  评论(0编辑  收藏  举报