ARC103F Distance Sums
题意
构造一棵树满足对于每一个点\(x\)到所有点的距离之和是\(d_x\),保证\(d\)两两不同。
传送门
\(n \le 10^5\)
思路
稍微良心一点,还是在能力范围之内的(复习英语\(within\space my \space reach\))
考虑一条边相连的两个点\(x\),设\(x\)到子树内所有点的距离为\(dis_x\),子节点个数为\(son_x\),\(y\)同理。这边的子树默认为一条边分割成两部分
\(d_x=dis_x+dis_y+son_y\)(走到\(y\)子树还要加上一条边)
\(d_y=dis_y+dis_x+son_x\)
那么两者应该相差\(son_y-son_x\)
刚开始一直在从\(d\)小的开始想,想到自闭。
换个思路我们发现,\(d\)最大的一定是叶子结点,因为它的\(son\)是最小的。
那就可以将\(d\)从大到小排序,从下往上连,而这个过程中\(son_x\)和\(d_x\)知道,那么\(son_y=n-son_x\),所以所需的\(d_y=d_x-2*son_x\)用\(map\)维护一下,每次找一下就好了,再把\(son\)值累加。如果说找不到合适的,那就没有方法了。
注意这样只满足了差值关系,所以我们还需要验证一下其中的某一个是否满足就好
#include <bits/stdc++.h>
const int N=100005;
int n,size[N],st[N],to[N<<1],tto[N],last[N],Next[N<<1],edge;
long long D;
struct note{
long long dis;
int x;
}d[N];
bool cmp(note x,note y){
return x.dis>y.dis;
}
using std::map;
map<long long,int> m;
void add(int x,int y){
to[++edge]=y;
Next[edge]=last[x];
last[x]=edge;
}
int dfs(int x,int fa,long long now){
D+=now;
for (int i=last[x];i;i=Next[i])
if (to[i]!=fa)
dfs(to[i],x,now+1);
}
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++) {
scanf("%lld",&d[i].dis);
d[i].x=i;
m[d[i].dis]=i;
}
std::sort(d+1,d+n+1,cmp);
for (int i=1;i<=n;i++) size[i]=1;
for (int i=1;i<n;i++){
long long dis=d[i].dis;
int x=d[i].x;
long long t=dis-abs(n-2*size[x]);
int fa=m.find(t)->second;
if (fa==x || !fa){
puts("-1");
return 0;
}
size[fa]+=size[x];
st[i]=x,tto[i]=fa;
add(x,fa),add(fa,x);
}
dfs(d[n].x,0,0);
if (D!=d[n].dis){
puts("-1");
return 0;
}
for (int i=1;i<n;i++) printf("%d %d\n",st[i],tto[i]);
}
后记
写完就要去跑步了,每天的\(jogging\)后吃饭真是让人舒心啊
* 生而自由 爱而无畏 *