[思维构造] 题解 Distance Sums

[思维构造] 题解 Distance Sums

题目链接

题目分析

注意到给出的数是互不相同的,这点是很重要的。

考虑重心满足所有点到重心的距离和最小,所以先排个序, \(D_i\) 最小的就是重心节点。

考虑以重心为根,得出每个点的子树大小 \(\mbox{sz}(i)\) ,如果 \(u\)\(v\) 的父亲,那么就会有 \(D_v=D_u+n-2\mbox{sz}(v)\)

注意到 \(D_i\) 最大的点肯定是叶子节点,也就是说它的 \(\mbox{sz}\) 是固定的,所以按 \(D_i\) 从大到小给每个节点找到它的父亲,由于 \(D_i\) 互不相同,从大到小为每个节点安排父亲节点是他们的父亲节点是固定的,所以每个节点的 \(\mbox{sz}\) 也是固定的,于是我们就可以构造出这样一棵树了,最后还需要判断一下所有点到重心的距离是否满足条件。

找父亲可以二分做,时间复杂度是 \(\mathcal O(n\log_2n)\) 的。

参考代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ch() getchar()
#define pc(x) putchar(x)
using namespace std;
template<typename T>void read(T&x){
	static char c;static int f;
	for(c=ch(),f=1;c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c>='0'&&c<='9';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>void write(T x){
	static char q[65];int cnt=0;
	if(x<0)pc('-'),x=-x;
	q[++cnt]=x%10,x/=10;
	while(x)
		q[++cnt]=x%10,x/=10;
	while(cnt)pc(q[cnt--]+'0');
}
const int maxn=100005;
struct Node{
	long long dis;int id;
	Node(long long dis=0,int id=0):
		dis(dis),id(id){}
	bool operator < (const Node o)const{
		return dis<o.dis;
	}
}P[maxn];
int sz[maxn];
int Find(int l,int r,long long dis){
	while(l<r){
		int mid=(l+r)>>1;
		if(P[mid].dis<dis)l=mid+1;else r=mid;
	}
	return P[l].dis==dis?l:-1;
}
int pa[maxn],dp[maxn];
int main(){
	int n;read(n);
	for(int i=1;i<=n;++i){
		read(P[i].dis);P[i].id=i;
	}
	sort(P+1,P+n+1);
	for(int i=n;i>=2;--i){
		++sz[i];
		long long sd=P[i].dis-(n-sz[i]*2);
		int j=Find(1,i-1,sd);
		if(~j){
			pa[P[i].id]=P[j].id;
			sz[j]+=sz[i];
		}
		else return puts("-1"),0;
	}
	long long sum=0;
	dp[P[1].id]=0;
	for(int i=2;i<=n;++i)
		sum+=dp[P[i].id]=dp[pa[P[i].id]]+1;
	if(sum!=P[1].dis)return puts("-1"),0;
	for(int i=2;i<=n;++i)
		write(P[i].id),pc(' '),write(pa[P[i].id]),pc('\n');
	return 0;
}

posted @ 2020-11-05 09:38  xiaolilsq  阅读(84)  评论(0编辑  收藏  举报