题目大意
给你一棵树,然后你需要找到一个n的排列,使得
相邻两个数在树中的距离和最大,输出这个距离
题解
不妨先单独的去考虑一条边,它把这一颗树分成了两个部分,我们经过这条边最多的次数就是其中点数较小的那一个部分
假设树只有一个重心,如果我们把起点放到重心处,每一次我们到的点都跨过重心,那么我们惊喜的发现除了重心到终点的路径少走了一次其他的点都走满了,而如果我们从其他的点开始一定不会更优,因为我们的终点是自己决定的,如果选其它的点必定会出现走到后面某一条路径不能通过起点的情况
把重心设做根,假设我们选择了重心之后随便走,那么有
ans=∑deep[x]∗2−deep[t]−∑deep[lca(dx,dx−1)]
a
n
s
=
∑
d
e
e
p
[
x
]
∗
2
−
d
e
e
p
[
t
]
−
∑
d
e
e
p
[
l
c
a
(
d
x
,
d
x
−
1
)
]
t表示最后一个点,d表示我们选择的行走序列
这个是显然的,因为每一次的路径都要走进来再走出去,但是最后一步走进来就结束了不会出去了,所以要减掉
根据我们上面所说的,最后一个lca可以全部变成根,那么现在我们只需要找到一个最小的deep[t] (t不能为根)就可以了
但是还有一种情况需要我们考虑,那就是重心不只有一个
在这样的情况中另一个重心为根的子树大小是总大小/2,也就是说如果我们要每一步都经过根节点那么最终一定是会在那个重心为根的子树中停止的,那么我们肯定选择离根最近的,也就是另一个重心
代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define ll long long
using namespace std;
const int maxn=1e5+5;
int fi[maxn],dui[maxn*2],ne[maxn*2],qc[maxn],dui1[maxn*2];
ll de[maxn],ans,mi;
int size[maxn];
int i,j,k,l,m,n,x,y,z,c1,c2,now;
void add(int x,int y,int z){
if (fi[x]==0) fi[x]=++now; else ne[qc[x]]=++now;
dui[now]=y; dui1[now]=z; qc[x]=now;
}
void dfs_size(int x,int y){
size[x]=1;
for(int i=fi[x];i;i=ne[i]){
if (dui[i]==y) continue;
dfs_size(dui[i],x);
size[x]=size[x]+size[dui[i]];
}
}
void dfs_root(int x,int y){
int z=size[1]-size[x];
for(int i=fi[x];i;i=ne[i]){
if (dui[i]==y) continue;
z=max(z,size[dui[i]]);
dfs_root(dui[i],x);
}
if (z<l){
l=z; c1=x; c2=0;
} else if (z==l) c2=x;
}
void dfs_dep(int x,int y){
for(int i=fi[x];i;i=ne[i]){
if (dui[i]==y) continue;
de[dui[i]]=de[x]+dui1[i];
dfs_dep(dui[i],x);
}
ans=ans+de[x]+de[x];
}
int main(){
// freopen("018d.in","r",stdin);
scanf("%d",&n);
fo(i,1,n-1){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z); add(y,x,z);
}
dfs_size(1,0);
l=n+1;
dfs_root(1,0);
dfs_dep(c1,0);
if (c2==0){
mi=1e16;
fo(i,1,n) if (i!=c1) mi=min(mi,de[i]);
} else mi=de[c2];
printf("%lld\n",ans-mi);
return 0;
}