CF468D Tree 题解

Codeforces
Luogu

P.S.

看上去好清真啊,看上去好原题啊,看上去好清新啊,洛谷评分也就一个紫,点开难度。
woc *3100,恐惧了恐惧了,爪巴了爪巴了。
和 @Krimson 一起胡的一个做法。

Description.

有一棵树,无根边带权。
一个排列的 \(\{p_i\}\) 权值定义为 \(\sum\text{dist}(i,p_i)\)
求权值最大值,以及取到这个最大值的最小排列。

Solution.

首先,最大值显然是 ATcoder 原题,肯定是每条边达到 \(\max\{sz_u,sz_v\}\)
然后第一问就做完了,证明就考虑取重心,然后反复横跳(AT 肯定是有原题的。

第二问考虑同样取重心,把树划分成了若干棵子树(重心本身可以当作一棵方便处理
考虑转化一下,定义一个树的入度表示这个树还没被匹配的点,出度表示这个树需要匹配的点。
那能匹配的方案肯定满足每个点都可以匹配,也就是其他点的入度大于当前点的出度。
我们维护 \(d_i\) 表示 其他点的入度减当前点的出度。
每次匹配相当于对匹配子树和被匹配子树的 \(d_i\) 不变,其他全局减一。
要求减完后不出现 \(-1\)

考虑如果出现了一个 \(d_i=0\),那肯定需要直接匹配这两个。
但是全局同一时间肯定仅可能有两个 \(d_i=0\),且如果有两个肯定其他已经匹配完了。

然后每次如果有除了当前子树外的 \(d_i=0\),那只能匹配目标。
否则就贪心匹配全局最小值,这样肯定不会让 \(d_i\) 变成负数。

至于全局 \(-1\) 直接维护一个 tag 就行了。

注意特判重心,因为重心可以和自己匹配,不需要满足 \(d_i\ge 0\) 的限制。

Coding.

点击查看代码
//Coded by leapfrog on 2021.10.28 {{{
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;char c=getchar(),f=0;
	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}//}}}
const int N=100005;set<int>nd[N],id;set<pair<int,int> >dd;int nid=0;
struct edge{int to,w,nxt;}e[N<<1];int et,head[N],d[N],p[N];
int n,sz[N],rt,dfn[N],nfd[N],dt,f[N],tag[N];ll rs=0;
inline void adde(int x,int y,int w) {e[++et]=(edge){y,w,head[x]},head[x]=et;}
inline void getrt(int x,int fa)
{
	sz[x]=1;int mx=0;for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa)
		getrt(e[i].to,x),sz[x]+=sz[e[i].to],mx=max(mx,sz[e[i].to]);
	mx=max(mx,n-sz[x]);if(mx<=n/2) rt=x;
}
inline void dfs0(int x,int fa)
{
	dfn[x]=++dt,nfd[dt]=x,sz[x]=1,f[x]=fa;
	for(int i=head[x];i;i=e[i].nxt) if(e[i].to!=fa)
		dfs0(e[i].to,x),sz[x]+=sz[e[i].to],rs+=2ll*min(sz[e[i].to],n-sz[e[i].to])*e[i].w;
}
int main()
{
	read(n);for(int i=1,x,y,w;i<n;i++) read(x,y,w),adde(x,y,w),adde(y,x,w);
	if(n==1) return puts("0\n1\n"),0;else if(n==2) return printf("%lld\n2 1\n",2ll*e[head[1]].w),0;
	getrt(1,0),dfs0(rt,0),printf("%lld\n",rs);
	for(int i=head[rt],x;i;i=e[i].nxt)
	{
		++nid,x=e[i].to;for(int l=dfn[x];l<dfn[x]+sz[x];l++) nd[tag[nfd[l]]=nid].insert(nfd[l]);
		id.insert(*nd[nid].begin()),dd.insert(make_pair(d[nid]=n-2*nd[nid].size(),nid));
	}
	nd[tag[rt]=++nid].insert(rt),dd.insert(make_pair(d[nid]=n-2,nid)),id.insert(rt);
	int tg=0;for(int i=1;i<=n;i++)
	{
		int ps=tag[i],tw;dd.erase(make_pair(d[ps],ps));
		if(dd.begin()->first+tg==0&&dd.begin()->second!=tag[rt]) tw=dd.begin()->second;
		else if(tag[*id.begin()]==ps&&i!=rt) tw=tag[*++id.begin()];else tw=tag[*id.begin()];
		int qw=*nd[tw].begin();p[i]=qw,tg--,dd.erase(make_pair(d[tw],tw));
		id.erase(qw),nd[tw].erase(nd[tw].begin());if(!nd[tw].empty()) id.insert(*nd[tw].begin());
		dd.insert(make_pair(++d[ps],ps)),dd.insert(make_pair(++d[tw],tw));
	}
	for(int i=1;i<=n;i++) printf("%d%c",p[i],i==n?'\n':' ');
	return 0;
}
posted @ 2021-10-29 08:01  Peal_Frog  阅读(50)  评论(0编辑  收藏  举报