题目
Polycarp 决定装饰他的房间,因为新年快到了。 Polycarp 将安装的主要装饰品之一是他将自己焊接的花环。
由一根电线连接的几盏灯组成的简单花环对 Polycarp 来说太无聊了。他要焊接一个由 n 个灯和 n-1 根电线组成的花环。恰好一个灯将连接到电网,并通过电线将电力从它传输到其他灯。每根电线恰好连接两个灯;一个灯被称为该电线的主灯(从其他电线获得电力并将其传输到该电线的灯),另一个称为辅助灯(从该电线获得电力的灯)。显然,每盏灯最多有一根电线为其供电(此灯是该电线的辅助灯,而所有其他电线直接连接到它的主灯)。
每个灯都有一个与之关联的亮度值,第 i 个灯的亮度为 2i。我们将电线的重要性定义为如果电线被切断(并且所有其他电线仍在工作),则所有与电网断开连接的灯的亮度值之和。
Polycarp 绘制了他想要制作的花环的方案(该方案描绘了所有 n 盏灯和 n-1 根电线,并且标记了将直接连接到电网的灯;电线的放置方式使得电源可以传输到每个灯)。之后,Polycarp 计算每根线的重要性,从 1 到 n-1 按重要性降序枚举,然后为每根线写下主灯的索引(从第一根线到最后一根线) )。
第二天,Polycarp 购买了花环的所有必需组件并决定焊接它——但他找不到方案。幸运的是,Polycarp 找到了所有电线的主灯索引列表。你能帮他恢复原计划吗?
输入
第一行包含一个整数 n (2≤n≤2⋅105) — 灯的数量。
第二行包含 n−1 个整数 a1, a2, …, an−1 (1≤ai≤n),其中 ai 是第 i 根电线的主灯索引(电线按降序编号重要性)。
输出
如果无法恢复原始方案,则打印一个整数 -1。
否则按如下方式打印方案。在第一行,打印一个整数 k (1≤k≤n) — 连接到电网的灯的索引。然后打印 n−1 行,每行包含两个整数 xi 和 yi (1≤xi,yi≤n, xi≠yi)——通过一些电线连接的灯的索引。可以按任何顺序打印电线(以及通过电线连接的灯)的描述。打印的描述必须与花环的方案相对应,这样 Polycarp 才能从中写出列表 a1、a2、…、an-1。如果有多个这样的方案,则输出其中任何一个。
题解思路
题目保证了这个图一定是树(即只有一个父节点)。
我们逆向使用拓扑排序,先用更小叶子节点来从后往前建树,这样就能保证题目中的每个点的权值情况,因为我们每一步都是用的最小的叶子节点。
入度清0时变成新的叶子节点。
逆向思维的一道构造题。
AC代码
/*从你的全世界路过.*/
#include <bits/stdc++.h>
//#include <unordered_map>
//priority_queue
#define PII pair<int,int>
#define ll long long
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 200100;
int in[N] ;
int a[N] ;
void solve()
{
priority_queue <int,vector<int>,greater<int>> q ;
int n ;
cin >> n ;
for (int i = 1 ; i < n ; i++ )
{
cin >> a[i] ;
in[a[i]]++ ;
}
for (int i = 1 ; i <= n ; i++ )
{
if (in[i] == 0 )
q.push(i) ;
}
cout << a[1] << "\n" ;
vector <PII> sk ;
for (int i = n - 1 ; i >= 1 ; i-- )
{
int t1 = q.top() ;
q.pop();
sk.push_back({a[i],t1}) ;
in[a[i]]--;
if (in[a[i]] == 0 )
q.push(a[i]) ;
}
reverse(sk.begin(),sk.end()) ;
for (auto i : sk )
cout << i.first << " " << i.second << "\n" ;
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
solve() ;
return 0 ;
}