CF1283F 题解

· · 题解

题意

一棵有 n 个点的树,对于一条边,记该边深度较大的一个端点为 u,该边的权值为 \sum 2^v,其中 vu 的子树中的点的编号。按边权从大到小的顺序给出每条边的深度较浅的端点,求树的根节点以及每条边的端点。

思路

由于点的编号是不重复的,对于两条没有祖孙关系的边,一条边的权值比另一条边大等价于这条边的子树中的点的编号最大值比另一条边的大。

可以用小根堆维护每个已经确定完边的子树中的树根以及子树中的最大值。初始时能确定哪些点是叶节点,将叶节点加入堆中。

按权值从小到大考虑每条边,确定当前边的另一端点为堆顶子树的根。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,a[200005],cnt[200005];
struct Node{
    int maxn,top;
    bool operator < (const Node &y)const{
        return maxn>y.maxn;
    }
};
priority_queue<Node>q;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<n;i++){
        cin>>a[i];
        cnt[a[i]]++;
    }
    for(int i=1;i<=n;i++){
        if(!cnt[i])q.push({i,i});
    }
    cout<<a[1]<<"\n";
    for(int i=n-1;i>=1;i--){
        Node k=q.top();
        q.pop();
        cout<<a[i]<<" "<<k.top<<"\n";
        cnt[a[i]]--;
        if(!cnt[a[i]])q.push({max(k.maxn,a[i]),a[i]}); 
    }
    return 0;
}