题解 CF1283F DIY Garland

题目由此去-luogu

题目由此去2-CF


题目大意:有 $ 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;
}
posted @ 2021-10-08 00:02  铭矾  阅读(37)  评论(0)    收藏  举报