题意
大致意思就是有一棵还未构建出来的树。
然后给出一个
D
i
Di
Di表示这棵树第i个点到所有点的距离和。
且保证
D
i
Di
Di互不相同,要求构造出这棵树。
思考历程
题意清新简短。
然鹅真滴恶心。
一开始成功发现一个性质,Di最小的点即为重心。
然后考虑将它作为一个根来搞。
然后就搞到头穿都没搞出来。
心态大崩。
题解
看完题解,犹如醍醐灌顶。
只能大呼妙哉。
重心确实可以当数的根,但是从根往周围拓展显然是个令人智熄的想法。
题解是反过来,从叶子推向根。
我们发现,由于d是不会重复的,而且一定有一个叶子节点的d是最大的。
抓住这个性质,我们就从大到小拍个序。
于是我们就从这个叶子节点开始。
我们发现,当前叶子节点的父亲只可能是:
d
[
f
a
]
=
d
[
i
]
−
(
n
−
s
i
z
[
i
]
)
+
s
i
z
[
i
]
d[fa]=d[i]-(n-siz[i])+siz[i]
d[fa]=d[i]−(n−siz[i])+siz[i]
其中
s
i
z
[
i
]
siz[i]
siz[i]表示i的子树大小。
意思其实很显然,类似于换根思想。
那么抓住这个性质我们就可以从大到小开始构造。
从大到小枚举,每次一个点我们用hash或别的什么找出它的父亲,标记,连边,维护siz。
然后如果一个点没被标记,则这个点也是个叶子节点,一起找父亲即可。
这样构造就没了。
注意两个东东:
1、如果在构造的时候找不到父亲或父亲是自己,那么就输出-1.
2、在构造完的时候还要从根开始走一遍判断根的d合不合法,因为邪恶的出题人可能会将所有的d加个数,然后就发现构造没问题,但距离却多了些诡异的东西。
不理解可以看看这个数据:
7
11
16
14
19
12
15
20
标程
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
const long long mo=50000007;
const int maxn=200010;
int n,m,id[maxn],fa[maxn];
long long d[maxn],siz[maxn],ans,js[maxn];
int tot,nex[maxn*2],las[maxn*2],tov[maxn*2];
int hs[mo+10];
void con(int x,int y)
{
tot++;
tov[tot]=y;
nex[tot]=las[x];
las[x]=tot;
}
void insert(long long x,int id)
{
long long i=x%mo;
while (hs[i]!=0)
{
i++;
if (i==mo) i=0;
}
hs[i]=id;
}
long long find(long long x)
{
long long i=x%mo;
while (hs[i]!=0 && d[hs[i]]!=x)
{
i++;
if (i==mo) i=0;
}
return hs[i];
}
void qsort(int l,int r)
{
int i=l;int j=r;
long long m=d[(i+j)/2];
while (i<=j)
{
while (d[i]>m) i++;
while (d[j]<m) j--;
if (i<=j)
{
swap(d[i],d[j]);
swap(id[i],id[j]);
i++;j--;
}
}
if (l<j) qsort(l,j);
if (r>i) qsort(i,r);
}
void dfs(int x,int ff)
{
js[x]=js[ff]+1;
for (int i=las[x];i;i=nex[i])
{
if (tov[i]!=ff)
{
dfs(tov[i],x);
ans+=js[x];
}
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%lld",&d[i]);
id[i]=i;
siz[i]=1;
}
qsort(1,n);
for (int i=1;i<=n;i++)
{
insert(d[i],i);
}
for (int i=1;i<n;i++)
{
long long op=d[i]-(n-2*siz[i]);
long long oq=find(op);
if (oq==0 || oq==i)
{
printf("-1\n");
return 0;
}
else
{
fa[i]=oq;
siz[oq]+=siz[i];
con(i,oq);con(oq,i);
}
}
dfs(n,0);
if (ans!=d[n])
{
printf("-1\n");
return 0;
}
else
{
for (int i=1;i<n;i++)
{
printf("%d %d\n",id[i],id[fa[i]]);
}
}
}