【Codeforces 1214E】Petya and Construction Set
构造
题目大意:
给你 $2n$ 个结点,要构造一棵树,边权为 $1$。
有 $n$ 个限制,第 $i$ 个限制是,编号为 $2i-1$ 和 $2i$ 的结点的距离必须为 $d_i$。
保证 $d_i\in[1,n]$。
要求输出一种符合条件的树。
题解:
构造题。
考虑按 $d_i$ 从大到小排序。
我们先预留一条链,前 $n$ 个位置留给编号为奇数的结点,后 $n$ 个位置留给编号为偶数的结点。
我们直接把奇数编号的结点按照 $d_i$ 的大小放入前 $n$ 个位置,大的放前面。然后依次考虑每个位置的限制。
设离当前这个位置最近的空着的留给编号为偶数的结点,离当前位置的距离为 $k$。
如果当前考虑的限制 $d_i=k$,则直接把它填到离当前位置距离为 $k$ 的那个空位上,然后对于下一个结点,离它最近的空位的距离显然还是 $k$。
否则,我们找一个已经放入结点的位置,作为待填结点的父亲,使得满足 $d_i$ 限制即可。此时,对于下一个结点,其 $k$ 比当前的 $k$ 小 $1$。
不难发现,这个算法执行的过程中,始终保证每个位置的 $d_i\leqslant k$,因此当 $d_i\neq k$ 时一定能找到一个符合条件的位置。所以构造方法正确。
时间复杂度 $O(n\log n)$ 或 $O(n)$。为了方便我直接使用了 sort
排序。
Code:
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
const int N=2e5+6;
pair<int,int>d[N],out[N];
int n,lst,tot,dist,L[N];
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;++i)
cin>>d[d[i].second=i].first;
sort(d+1,d+n+1,greater<pair<int,int> >());
for(int i=1;i<n;++i)
out[++tot]=make_pair(d[i].second*2-1,d[i+1].second*2-1);
for(int i=1;i<=n;++i)L[i]=d[i].second*2-1;
lst=n;
dist=n;
for(int i=1;i<=n;++i){
if(d[i].first==dist){
out[++tot]=make_pair(L[lst],d[i].second*2);L[++lst]=d[i].second*2;
}else{
int pos=i+d[i].first-1;
out[++tot]=make_pair(L[pos],d[i].second*2);
--dist;
}
}
for(int i=1;i<=tot;++i)
cout<<out[i].first<<' '<<out[i].second<<'\n';
return 0;
}