1 条题解
-
0
题目理解
题意:
给一棵 个结点的树(无向边)。
我们想把每条无向边定向(变成有向边),这样就能得到一些“好的”有序对 ,表示 可以沿着有向边走到 。定义:
- 如果 到 之间有一条有向路径,那么 是“好”的。
- 初始时,每条边定向后,至少会给出两个方向中的一个(因为一条边只能定一个方向,所以 条边至少给 个好对——注意这里每个有序对是一个方向)。
- 题目要求:恰好有 个好对。
关键推导
1. 最少好对数
在树中,每条边定向后,恰好产生一个方向的好对(比如 或 ),所以至少 个好对。
我们需要多一个好对,即总共有 个好对。
2. 好对的长度分析
- 长度 的好对正好就是定向后的边(有向边)。
- 所以要想多一个好对,必须有一个长度至少为 的路径(比如 )。
- 如果路径长度大于 (比如 ),那么它的子路径(比如 或 )也会成为好对,这样就会超过 个。
因此,唯一可能的结构是:
只有所有有向边(长度1)和一个长度为 2 的路径 。
3. 中间点 的限制
考虑长度为 2 的路径的中间点 。
如果 还有另一个邻居 (不同于 和 ),那么:- 如果 ,那么 是长度为 2 的好对,多了一个。
- 如果 ,那么 是长度为 2 的好对,多了一个。
不管怎样,都会产生第二个长度为 2 的好对,加上原来的那个就变成 个好对了(因为原来的长度1有 个,加上两个长度2,一共 )。
所以 的度数必须恰好为 2。
4. 结论
- 原树中必须有一个度数为 2 的点 ,否则无解。
- 如果有,就可以构造。
构造方法
- 找到度数为 2 的点 。设它的两个邻点为 和 。
- 定向:
- 从 出发:所有其它边都从 指向外(即 对于 )。
- 向 汇聚:所有其它边都指向 (即 对于 )。
- 递归处理:
- 对于 的其它邻居 ,它们作为新的“起点”,向外指(相对于它们的父边)? 其实在实现中,是 dfs 交替方向。
为什么不会产生额外好对?
因为除了 这条长度 2 的路径,任何其他路径要么起点出度大但只能走到 方向,要么终点入度大但只能从 方向进入,不会形成第二个长度 2 的好对。
代码实现
输入与建图
int n; vector<int> g[N];- 邻接表存树。
找度数为 2 的点
int r = 0; while (r < n && sz(g[r]) != 2) r++; if (r >= n) { cout << "NO\n"; return; }- 如果不存在度数为 2 的点,输出 NO。
定向
ans.emplace_back(r, g[r][0]); // r -> a ans.emplace_back(g[r][1], r); // b -> r- 的两个邻居:(记为 ),(记为 )。
DFS 构造
void dfs(int v, bool in) { used[v] = 1; for (int to : g[v]) { if (used[to]) continue; if (in) ans.emplace_back(to, v); // to -> v (汇聚) else ans.emplace_back(v, to); // v -> to (外指) dfs(to, !in); } }- 从 出发,用
in = true,意思是 的邻居要指向 (汇聚方向)。 - 从 出发,用
in = false,意思是 的邻居要从 向外指。
为什么这样交替?
因为我们要保证除了 外,其他路径都不会有长度 2 的好对。
这个 DFS 模式实际是在树的两个分支上做“远离 r 的方向”,并交替方向,从而不会产生新的 路径。
输出
cout << "YES\n"; sort(ans.begin(), ans.end()); for (auto [u, v] : ans) cout << u+1 << " " << v+1 << '\n';- 排序是为了输出稳定,无顺序要求可省略。
总结
- 必要条件:存在度数为 2 的点。
- 构造方法:以该点为中间点,把树分成两半,一边向外指,一边向内指。
- 时间复杂度:。
- 1
信息
- ID
- 6548
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 4
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者