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\)后吃饭真是让人舒心啊

posted @ 2019-11-05 16:41  flyfeather  阅读(214)  评论(0编辑  收藏  举报