http://codeforces.com/problemset/problem/1214/E
题目大意:你需要构造出一个节点数为
2
n
2n
2n的一棵树. 给出
n
n
n个范围在
[
1
,
n
]
[1,n]
[1,n]的正整数
d
i
d_{i}
di。你构造出来的树需要满足:第
2
i
−
1
2i−1
2i−1个点与第
2
i
2i
2i 个点在树上的距离恰为
d
i
d_{i}
di。这里定义两个点
u
u
u ,
v
v
v 之间的距离为
u
u
u到
v
v
v路径上的边数.如果有多种方案,输出任意一种即可。
思路:因为给定的距离只关系到 2 i 2i 2i与 2 i − 1 2i-1 2i−1,因此考虑先把偶数点(或奇数点)全部拿出来构成一条链,再决定剩余点怎么加。当然这条链不是随便构成的,应该按照 d d d从大到小排序,这样我们一定可以构造出满足题意的树。严格的证明我是不会,但是可以大概解释一下。我们从 d i < = n d_{i}<=n di<=n这个性质入手,因为我们先处理的是 d i d_{i} di比较大的点,此时如果距离 < n <n <n,那么一定可以在该链的某个点上加一个分叉点来实现,如果距离等于 n n n,那么一定可以把这个点加到当前链的末尾,递推下去不难发现是一定有解的。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define pr pair<int,int>
using namespace std;
const int maxn=1e5+5;
struct node
{
int id,v;
bool operator <(const node &a)const
{
return v>a.v;
}
}a[maxn<<1];
vector<pr> vec;
int n;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i].v),a[i].id=2*i;
sort(a+1,a+1+n);
for(int i=1;i<n;i++)
vec.push_back(pr(a[i].id,a[i+1].id));
int len=n,tmp;
for(int i=1;i<=n;i++)
{
tmp=a[i+a[i].v-1].id;
if(tmp==a[len].id)//加到链尾上
a[++len].id=a[i].id-1;
vec.push_back(pr(a[i].id-1,tmp));
}
len=vec.size();
for(int i=0;i<len;i++)
printf("%d %d\n",vec[i].first,vec[i].second);
return 0;
}