zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
  • 相关阅读:
    面向机器学习的特征工程
    卷积可视化,图像理解,deepdream,风格迁移
    损失函数
    开源是如何支撑区块链技术发展的
    揭秘机密计算:弥补最后一个入侵 “漏洞”
    什么是超大规模数据中心?
    比特币是避险资产还是储备资产?
    区块链技术生态持续优化,五大趋势不容忽视
    物联网低功耗广域网(LPWAN)的比较
    碎片化是物联网快速发展的阻碍,也是机会
  • 原文地址:https://www.cnblogs.com/ZigZagKmp/p/14374450.html
Copyright © 2011-2022 走看看