传送门
解析:
本次构造题大赛的最后一道。
真的毒瘤啊,前面给一个那么扯的DDD题,后面又给一个这么神仙的结论题。
思路:
首先拿到题我整个人是方的,根本不知道如何下手。。。
找重心?明显假了啊。。。
到所有点距离和最小的节点?似乎没什么好用的性质。
到所有点距离和最大的节点?一定是一个叶子!
考虑它不是一个叶子的情况,我们一定可以将它向叶子方向移动,从而得到一个更大的距离和。
那么我们考虑怎么找这个叶子的父亲。
满足Dw=Dv−(n−2)D_w=D_v-(n-2)Dw=Dv−(n−2)的是这个叶子节点的父亲。
考虑将该叶子节点向上挪一个单位的距离则新的节点到其他节点的距离各减少1,而这两个节点之间的距离仍然是111,所以我们这样就找到了它的父亲。
为了便于叙述,我们不失一般性地定义D1<D2<⋅⋅⋅<DND_1 < D_2 < · · · < D_ND1<D2<⋅⋅⋅<DN。
并且将这棵树定义为以111为根节点的有根树。
我们采用倒叙枚举,对每个节点(除根节点)确定它的父亲。
那么接下来才是真正的结论:
从树上移走一条边<u,v><u,v><u,v>得到两个联通分量大小定义为nu,nvn_u,n_vnu,nv则Du,DvD_u,D_vDu,Dv有如下关系Du+nu=Dv+nvD_u+n_u=D_v+n_vDu+nu=Dv+nv
证明很简单,考虑将uuu向vvv移动一个单位,那么它离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+nv−nu=Dv+N−2×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;
}