2018.09.30【Atcoder Regular Contest103】F - Distance Sums(构造)(数学推理)

传送门


解析:

本次构造题大赛的最后一道。
真的毒瘤啊,前面给一个那么扯的DDD,后面又给一个这么神仙的结论题。

思路:

首先拿到题我整个人是方的,根本不知道如何下手。。。
找重心?明显假了啊。。。

到所有点距离和最小的节点?似乎没什么好用的性质。

到所有点距离和最大的节点?一定是一个叶子!

考虑它不是一个叶子的情况,我们一定可以将它向叶子方向移动,从而得到一个更大的距离和。

那么我们考虑怎么找这个叶子的父亲。
满足Dw=Dv−(n−2)D_w=D_v-(n-2)Dw=Dv(n2)的是这个叶子节点的父亲。
考虑将该叶子节点向上挪一个单位的距离则新的节点到其他节点的距离各减少1,而这两个节点之间的距离仍然是111,所以我们这样就找到了它的父亲。

为了便于叙述,我们不失一般性地定义D1&lt;D2&lt;⋅⋅⋅&lt;DND_1 &lt; D_2 &lt; · · · &lt; D_ND1<D2<<DN
并且将这棵树定义为以111为根节点的有根树。

我们采用倒叙枚举,对每个节点(除根节点)确定它的父亲。

那么接下来才是真正的结论:
从树上移走一条边&lt;u,v&gt;&lt;u,v&gt;<u,v>得到两个联通分量大小定义为nu,nvn_u,n_vnu,nvDu,DvD_u,D_vDu,Dv有如下关系Du+nu=Dv+nvD_u+n_u=D_v+n_vDu+nu=Dv+nv

证明很简单,考虑将uuuvvv移动一个单位,那么它离nvn_vnv中的所有节点近一个单位,离nun_unu中所有节点远一个单位。

那么我们将上面这个式子变形Du=Dv+nv−nu=Dv+N−2×nuD_u=D_v+n_v-n_u=D_v+N-2\times n_uDu=Dv+nvnu=Dv+N2×nu

那么我们就可以通过由下向上的递推来确定每一个uuu的父亲vvv了。
中间判断一下会不会出现父亲的子树大小小于儿子,非根节点没有父亲这两种情况就行了。

最后再判断一下根节点的距离限制是否满足。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
ll getint(){
	re ll num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

inline
void outint(int a){
	static char ch[13];
	if(a==0)pc('0');
	while(a)ch[++ch[0]]=a-a/10*10,a/=10;
	while(ch[0])pc(ch[ch[0]--]^48);
}

cs int N=100005;

int last[N],nxt[N],to[N],ecnt;
inline
void addedge(int u,int v){
	nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v;
}

map<ll,int> id;
int n;
ll d[N];
int siz[N];

int dist[N];
inline
void dfs(int u){
	for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
		dist[v]=dist[u]+1;
		dfs(v);
	}
}

vector<pair<int,int> > edge;

signed main(){
	n=getint();
	for(int re i=1;i<=n;++i)d[i]=getint(),id[d[i]]=i;
	
	sort(d+1,d+n+1);
	for(int re i=1;i<=n;++i)siz[i]=1;
	
	for(int re i=n;i>1;--i){
		int u=id[d[i]];
		int tmp=n-2*siz[u];
		if(tmp<=0)return puts("-1"),0; 
		ll t=d[i]-tmp;
		if(id.count(t)==0)return puts("-1"),0;
		int pos=id[t];
		addedge(pos,u);
		siz[pos]+=siz[u];
		edge.push_back(make_pair(pos,u));
	}
	ll sum=0;
	dfs(id[d[1]]);
	for(int re i=1;i<=n;++i){
		sum+=dist[i];
	}
	if(sum!=d[1])return puts("-1"),0;
	for(int re i=0;i<edge.size();++i)outint(edge[i].first),pc(' '),outint(edge[i].second),pc('\n');
	return 0;
}

转载于:https://www.cnblogs.com/zxyoi/p/10047275.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值