ARC103F Distance Sums

题意

构造一棵树满足对于每一个点(x)到所有点的距离之和是(d_x),保证(d)两两不同。
传送门
(n le 10^5)

思路

稍微良心一点,还是在能力范围之内的(复习英语(withinspace 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
",st[i],tto[i]);
}

后记

写完就要去跑步了,每天的(jogging)后吃饭真是让人舒心啊

原文地址:https://www.cnblogs.com/flyfeather6/p/11799467.html