• 【AT4434】[ARC103D] Distance Sums(构造)


    点此看题面

    • 给定一棵(n)个点的树中每个点到其余所有点的距离之和(保证互不相同),要求构造一棵合法的树或判无解。
    • (nle10^5)

    树的重心性质

    我们记录每个点的子树大小为(Sz_x)

    考虑一个点向上走到所有点距离之和的变化量,到子树内的(Sz_x)个点增加了(1),到子树外的(n-Sz_x)个点减少了(1),因此一共变化了(2 imes Sz_x-n)

    看到距离之和,由于越靠近树的重心到所有点距离之和越小,所以最小的那个距离对应的必然是树的重心,我们不妨以它为根。

    这样一来,我们按照距离之和从大到小枚举每个点,那么子节点必然在父节点之前被枚举到。

    也就是说,枚举到一个点的时候,已经能确定出(Sz_x)了,则到所有点距离之和等于(D_x+2 imes Sz_x-n)的点就是(x)的父节点了(可以用(map)简单维护),因为保证距离之和互不相同,这样的点最多只有一个(如果不存在说明无解)。

    一波下来我们就确定了树的形态,但还要注意我们只是保证了子节点与父节点距离之和的差值是正确的,最后还要求一下根节点到所有点的距离之和判一下与给出是否相同。

    我们可以记录一个(s_x)表示每个点到子树内所有点的距离之和,确定父子关系给(Sz_{fa})加上(Sz_x)的同时给(s_{fa})加上(s_x+Sz_x),则上面一波下来也同时求出了根节点到所有点的距离之和,就不用额外再建树(dfs)了。

    代码:(O(nlogn))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define LL long long
    using namespace std;
    int n,fa[N+5],Sz[N+5];LL a[N+5],s[N+5];map<LL,int> p;map<LL,int>::iterator it;
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	Tp I void write(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);}
    	Tp I void writeln(Con Ty& x,Con Ty& y) {write(x),pc(' '),write(y),pc('
    ');}
    }using namespace FastIO;
    int main()
    {
    	RI i;for(read(n),i=1;i<=n;++i) read(a[i]),Sz[p[a[i]]=i]=1;//存到map中
    	RI x;LL t;it=p.end();W(--it,Sz[x=it->second]^n)//按距离之和从大到小枚举
    		if(2*Sz[x]>=n||!p.count(t=a[x]+2*Sz[x]-n)) return puts("-1"),0;//如果父节点距离之和没有变小或不存在对应距离之和的节点无解
    		else Sz[fa[x]=p[t]]+=Sz[x],s[fa[x]]+=s[x]+Sz[x];//更新父节点子树大小,更新父节点到子树内所有点的距离之和
    	if(x=it->second,a[x]^s[x]) return puts("-1"),0;//检验根节点到所有点的距离之和是否正确
    	for(i=1;i<=n;++i) fa[i]&&(writeln(fa[i],i),0);return clear(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    如何在Nginx下配置PHP程序环境
    PHPStorm2016如何安装主题
    Yii2初级入门教程
    递归递归!!
    PHP值传递和引用传递的区别
    网站移植到linux上后常犯的错误
    PHP之function_handling 函数
    PHP如何使用免费在线客服插件
    PHP快速入门 常用配置
    PHP快速入门 如何操作MySQL
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/AT4434.html
Copyright © 2020-2023  润新知