原题链接:F. DIY Garland
题目大意:
给出一棵 n n n 个点的树,第 i i i 个点的权值为 2 i 2^{i} 2i ,定义第 j j j 条边的权值为其连接的两点 u , v u,v u,v 中深度较大的点的子树的权值和。
边按照边权由大到小的顺序给出每条边深度较小的点的编号,现在请构造一棵满足如上情况的原树,输出根以及连边情况。
解题思路:
注意到第 i i i 个点的点权是 2 i 2^{i} 2i,也就是说不会有点权重合的情况,且点权都互相严格大于或小于。
又因为边是由边权大到边权小的顺序给出的,那么我们第一条边深度较浅的点一定是根节点。
感性证明一下,由于每个点点权都是 2 i 2^{i} 2i 的形式,那么我们点权的加和也一定互不相同,即每条边边权都不同,假设边权为最大的边 ( v , y ) (v,y) (v,y) 所连深度较小的点( 我们设为 v v v )不是根节点,那么我们根节点( 我们设为 r o o t root root )由于是整棵树的根,一定有 d e p r o o t < d e p v dep_{root} < dep_{v} deproot<depv,设 v v v 的某个父亲 x x x 和根节点 r o o t root root 有直接连边 ( r o o t , x ) (root,x) (root,x) ,又边权互不同,那么我们一定会存在一条边 ( r o o t , x ) (root,x) (root,x) 的权值大于 ( v , y ) (v,y) (v,y) 的权值,和 ( v , y ) (v,y) (v,y) 的权值最大矛盾,故权值最大的边的最小点一定是根节点 r o o t root root,证毕。
考虑怎么构造:注意到一个问题,我们都只会获得深度较小的点,而那些边所连的点一定会有一些深度较大的点不会在输入中出现,而这些点一定是这棵树的叶子节点。
一个点在输入中的出现次数一定是它子树中儿子节点的个数,我们先将其较小的子树构造完后再构造它的子树,这样做是最方便的,而且也是有正确性的。
我们开一个按编号排序的小根堆,将所有叶子都保存进去,然后倒序枚举给出的边集,让权值最小的边深度最小的点和权值最小的叶子节点相连,当其子树内的点都连完时,它又成为了叶子节点,扔入小根堆,可知这样可以构造一组合法的解( 因为 2 i > 2 i − 1 + 2 i − 2 + . . . + 2 1 2^{i}>2^{i-1}+2^{i-2}+...+2^{1} 2i>2i−1+2i−2+...+21 ,所以这样贪心一定会使得按顺序构造的边 w 1 < w 2 < . . . < w n w_{1}<w_{2}<...<w_{n} w1<w2<...<wn)。
时间复杂度: O ( n log n ) O(n \log n) O(nlogn)
AC代码:
#include <bits/stdc++.h>
#define YES return void(cout << "Yes\n")
#define NO return void(cout << "No\n")
using namespace std;
using u64 = unsigned long long;
using PII = pair<int, int>;
using i64 = long long;
void solve() {
int n;
cin >> n;
priority_queue<int, vector<int>, greater<int>> heap;
vector<int> arr(n - 1), in(n + 1);
for (auto& v : arr) {
cin >> v;
++in[v];
}
for (int i = 1; i <= n; ++i) {
if (!in[i]) {
heap.push(i);
}
}
reverse(arr.begin(), arr.end());
vector<PII> edge;
for (auto& u : arr) {
edge.emplace_back(u, heap.top());
heap.pop();
if (--in[u] == 0) {
heap.push(u);
}
}
cout << arr.back() << '\n';
while (edge.size()) {
auto [u, v] = edge.back();
cout << u << " " << v << '\n';
edge.pop_back();
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1; //cin >> t;
while (t--) solve();
return 0;
}