题解 CF1283F DIY Garland
题目大意:有 $ n $ 个点,第 $ i $ 个点的权值为 $ i^2 $ ,现在有 $ n-1 $ 条边使得这些点组成一棵树,边的权值是它连接的子树的权值和。现在按照边的权值从大到小给你每条边的父节点,让你输出根节点的下标以及输出输入顺序每条边所连接的两个点。
思路:
-
自底向上很简单,首先我们处理出所有未出现过的点是什么,然后最后一条边连接的一定是最小的那个点。
-
有了这个思路我们就可以很容易的想到如果我们从后往前做,那么当前的点连接的一定是可供选择的点中的子树的值最小的那个。
-
由于每个点的值是 $ 2 $ 的幂次,于是我们只需要记录当前点子树中最大的点的值是什么即可。
-
那么什么是可供选择的点?首先可供选择的点一定是未在输入中出现过的点,然后如果当前点还在前面的输入中出现过,那么就证明这个点还连着值更大的儿子,那么这个点就不能被判为可供选择的点。
code:
#include <cstdio>
#include <queue>
#include <iostream>
#include <algorithm>
#define pii pair<int, int>
#define fst first
#define snd second
using namespace std;
const int N = 2e5 + 5;
struct node {
int id, mx;
bool operator< (const node& a)const {return mx > a.mx;}
};
priority_queue<node> q;
int n, a[N], vis[N];
pii ans[N];
int main() {
scanf("%d", &n);
for (int i=1; i<n; i++) {
scanf("%d", &a[i]);
vis[a[i]]++ ;
}
for (int i=1; i<=n; i++) {
if (!vis[i]) q.push({i, i});
}
for (int i=n-1; i; i--) {
node now = q.top();
q.pop();
ans[i] = {a[i], now.id};
vis[a[i]]-- ;
if (a[i] != a[1] && !vis[a[i]]) q.push({a[i], max(now.mx, a[i])});
}
printf("%d\n", a[1]);
for (int i=1; i<n; i++) {
printf("%d %d\n", ans[i].fst, ans[i].snd);
}
return 0;
}