题面描述
已知整数
n
n
n。
f
i
=
∑
j
=
1
n
d
i
s
(
i
,
j
)
f_i=\sum_{j=1}^n dis(i,j)
fi=∑j=1ndis(i,j),已知
f
1
,
f
2
,
⋯
f
n
f_1,f_2, \cdots f_n
f1,f2,⋯fn,且对于任意的
1
⩽
i
<
j
⩽
n
1 \leqslant i < j \leqslant n
1⩽i<j⩽n 满足
f
i
≠
f
j
f_i ≠ f_j
fi=fj。构造一颗由
n
n
n 个节点构成的树满足对于每个节点的
f
f
f 值与给定的值相同,或输出无解。
1
⩽
n
⩽
1
0
5
,
0
⩽
f
i
⩽
1
0
12
1 \leqslant n \leqslant 10^5,0 \leqslant f_i \leqslant 10^{12}
1⩽n⩽105,0⩽fi⩽1012
时间限制1s,空间限制512MB。
题解
若有解,则
f
f
f 值最小的点一定是树的重心,
f
f
f 值最大的点一定是叶子。不妨以重心为根。若点
i
i
i 为点
j
j
j 的父亲节点,则一定有
f
i
<
f
j
f_i<f_j
fi<fj。当
j
j
j 为根算出
f
j
f_j
fj 后,换根到
i
i
i 时,
i
i
i 到
j
j
j 之间的边的贡献从
n
−
s
i
z
e
j
n-size_j
n−sizej 变为了
s
i
z
e
j
size_j
sizej,其他边的贡献不变。因此
f
i
=
f
j
+
2
s
i
z
e
j
−
n
f_i=f_j+2size_j-n
fi=fj+2sizej−n。将点按
f
f
f 值排序,从
f
f
f 值最大的点开始,找它的父亲并维护其
s
i
z
e
size
size。通过map等STL可以做到时间复杂度
O
(
n
log
2
n
)
O(n \log_2n)
O(nlog2n)。
然而,当
f
f
f 值相对大小不变时,树是可能无解的,因此随便选一个点当根求一次
f
f
f 并与输入的
f
f
f 值进行比对,时间复杂度
O
(
n
)
O(n)
O(n)。
所以总时间复杂度
O
(
n
log
2
n
)
O(n \log_2n)
O(nlog2n),空间复杂度
O
(
n
)
O(n)
O(n)。
#include<stdio.h>
#include<map>
#include<algorithm>
#include<vector>
using namespace std;
#define R register int
#define L long long
#define N 100001
L f[N],f2[N],q[N];
int fa[N],sz[N];
vector<int>G[N];
inline void Check(int x){
for(vector<int>::iterator T=G[x].begin();T!=G[x].end();T++){
Check(*T);
f2[x]+=f2[*T]+sz[*T];
}
}
int main(){
int n;
scanf("%d",&n);
map<L,int>P;
for(R i=1;i<=n;i++){
scanf("%lld",f+i);
P[f[i]]=i;
q[i-1]=f[i];
sz[i]=1;
}
sort(q,q+n);
for(R i=n-1;i!=0;i--){
int g=P[q[i]];
L t=q[i]+(sz[g]<<1)-n;
if(P[t]==0){
printf("-1");
return 0;
}
fa[g]=P[t];
sz[fa[g]]+=sz[g];
G[fa[g]].push_back(g);
}
int root=P.begin()->second;
Check(root);
if(f2[root]==f[root]){
for(R i=1;i<=n;i++){
if(i!=root){
printf("%d %d\n",i,fa[i]);
}
}
}else{
printf("-1");
}
return 0;
}