ARC103D题解

题意简述

给定 (n) 个互不相同的整数 (D_i),表示一棵树每一个节点到其他所有节点的简单路径距离和。

构造出原树,或判断这是不可能的。

(nle 10^5)

算法分析

乍看上去没有任何思路,考虑先找一些性质。

首先我们考虑两个相邻的节点 (x,y),并假设 (y)(x) 的父节点,那么不难发现 (D(x)=D(y)-siz(x)+left(n-siz(x) ight)),即 (D(x)=D(y)-2siz(x)+n)

由这个性质,我们不难发现,所有 (D_i) 中最大的一定是一个叶子节点的 (D_i)

因此我们考虑将这棵树自底向上构造出来。首先先对 (D_i) 从大到小排序,然后维护一个森林,存储每一棵树的根节点,树大小,树根的 (D_i) ,这样我们可以求出每一棵树要连接的父节点(D_i)。对于一个新加的节点,我们找到所有可以作为其儿子的树,连边即可。最后连 (n-1) 条边即可。

下面我们考虑证明这个构造的正确性。首先我们要证明对于任意一棵树,都能找到一个节点,使得从这个节点所有叶子的 (D_i) 递增。

(D_i)的递推式与 (siz) 有关,因此我们直接取树的重心,由树的重心性质不难发现,其除了其本身以外所有节点的 (2 imes siz<n),因此 (D(x)>D(y))

接着我们要证明,如果有解,上面的构造一定能得到一个可行解。

由于 (D_i) 互不相同,所以如果对于当前枚举的节点 (i) ,有一棵树的根 (x) 满足连边条件却不连边,以后一定没有连边的机会,因此一定要连边才可能有解。这样的构造从根到叶子 (D_i) 递增,由上证单调性可知对于任意有解的树,这样的构造一定能将其构造出来。

但由于一些边界问题,上述构造得到的解不一定满足题意,我们需要再检验一次。

找连边的节点可以用 set 维护,每一个元素会进入离开set一次,均摊下来复杂度为 (O(nlog n))

代码实现

#include<bits/stdc++.h>
using namespace std;
#define maxn 1000005
#define maxm 2000005
#define inf 0x3f3f3f3f
#define int long long
#define mod 1000000007
#define local
void file(string s){freopen((s+".in").c_str(),"r",stdin);freopen((s+".out").c_str(),"w",stdout);}
template <typename Tp> void read(Tp &x){
	int fh=1;char c=getchar();x=0;
	while(c>'9'||c<'0'){if(c=='-'){fh=-1;}c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c&15);c=getchar();}x*=fh;
}
int n,m;
struct node{
	int id,vl;
	bool operator <(node y)const{
		return vl>y.vl;
	}
}a[maxn];
int edf[maxn],edt[maxn],ett;
struct nds{
	int rt,siz,vl;
	bool operator <(nds y)const{//按照应该的父节点di排序
		return ((vl+2*siz-n)==(y.vl+2*y.siz-n))?(rt<y.rt):((vl+2*siz-n)<(y.vl+2*y.siz-n));
	}
};
set<nds>st;
namespace CHK{//二次检验
	vector<int>G[maxn];
	int d[maxn],siz[maxn];
	void dfs1(int x,int pa){
		siz[x]=1;
		for(auto y:G[x]){
			if(y==pa)continue;
			dfs1(y,x);
			siz[x]+=siz[y];
			d[x]+=d[y]+siz[y];
		}
	}
	void dfs2(int x,int pa){
		for(auto y:G[x]){
			if(y==pa)continue;
			d[y]=d[x]-2*siz[y]+n;
			dfs2(y,x);
		}
	}
	void chk(){
		for(int i=1;i<=ett;++i){
			G[edf[i]].push_back(edt[i]);
			G[edt[i]].push_back(edf[i]);
		}
		dfs1(1,0);dfs2(1,0);
		for(int i=1;i<=n;++i){
			int x=a[i].id,dd=a[i].vl;
			if(d[x]!=dd){
				puts("-1");
				exit(0);
			}
		}
	}
}
signed main(){
	read(n);
	for(int i=1;i<=n;++i){
		read(a[i].vl);a[i].id=i;
	}
	sort(a+1,a+n+1);
	set<nds>::iterator itl,itr,it,it0;
	for(int i=1;i<=n;++i){
		if(!st.size()){
			st.insert((nds){a[i].id,1,a[i].vl});
			continue;
		}
		int tvl=a[i].vl,tn=(n>>1);
		if(n&1)++tvl;//这里是用set写不太方便的一个地方,这里强行构造出一个siz,vl使得vl+2*siz-n=di
		itl=st.lower_bound((nds){0,tn,tvl});
		itr=st.upper_bound((nds){n+1,tn,tvl});
		if(itl==itr){
			st.insert((nds){a[i].id,1,a[i].vl});
			continue;
		}
		int sm=1;
		for(it=itl;it!=itr;++it){
			++ett;
			edf[ett]=a[i].id;
			edt[ett]=(*it).rt;
			sm+=(*it).siz;
		}
		st.insert((nds){a[i].id,sm,a[i].vl});
		for(it=itl;it!=itr;){
			it0=it;++it;
			st.erase(it0);
		}
	}
	if(st.size()>1){
		puts("-1");
	}
	else{
		CHK::chk(); 
		for(int i=1;i<=ett;++i)printf("%lld %lld
",edf[i],edt[i]);
	}
	return 0;
}

原文地址:https://www.cnblogs.com/ZigZagKmp/p/14374450.html