题意:一棵nnn个点的无权树,给定每个点iii到其他所有点的距离之和did_idi,保证did_idi两两不同。构造或判断无法构造一棵满足条件的树。
n≤105n\leq 10^5n≤105
首先对于非根结点uuu,有
du=dfau−sizu+(n−sizu)d_u=d_{fa_u}-siz_u+(n-siz_u)du=dfau−sizu+(n−sizu)
所以ddd最大的一定是叶子结点
把ddd从大到小排序,然后用上面的式子依次找到每个点的父亲,如果没有找到输出无解。
注意并不是由儿子的ddd来确定它的父亲是谁,它的父亲以及ddd值是早就定好了的,你只是把它找出来。这个式子是计算式而不是决定式。
因为ddd值是唯一的,所以这个过程是确定的,如果有解的话一定可以确定出来。
注意构造出来了不一定代表有解,如果把ddd看成nnn个变量,而n−1n-1n−1条边只能确定ddd之间的关系。所以需要判断一个点算出的ddd是否是真正的ddd,直接把sizsizsiz加起来就是drootd_{root}droot,判一下即可。
复杂度O(nlogn)O(n\log n)O(nlogn)
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <utility>
#define MAXN 100005
using namespace std;
typedef long long ll;
typedef pair<ll,int> pi;
ll read()
{ll ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
pi p[MAXN];
int siz[MAXN],fa[MAXN];
ll d[MAXN];
int main()
{int n=read();for (int i=1;i<=n;i++) p[i]=make_pair(read(),i),siz[i]=1;sort(p+1,p+n+1);ll sum=0;for (int i=n;i>1;i--){ll t=p[i].first-n+2*siz[p[i].second];int k=lower_bound(p+1,p+n+1,make_pair(t,0))-p;if (p[k].first!=t) return puts("-1"),0;fa[p[i].second]=p[k].second,siz[p[k].second]+=siz[p[i].second];sum+=siz[p[i].second];}if (sum!=p[1].first) return puts("-1"),0;for (int i=1;i<=n;i++) if (fa[i]) printf("%d %d\n",fa[i],i);return 0;
}