Codeforces Round #611 (Div. 3) F 拓扑排序 构造树 思维

43 篇文章 1 订阅

题目

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 ;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值