感性理解出来这么一个东西:
给定一棵非负权树,对于任意一点,一定存在一条以它为起点的最长链过直径中点(这里的中点可以是边)
用反证法推一下好像是对的:
如果以直径中点为根(是边的话可以分类讨论两个点),那么若有一个点的最长链全部都不经过根,那么如果把这条链连到根直径就会变长肯定不对
并且这个结论推广到直径任何一点上好像都是对的
那么是否可以这样解决这个问题:
给定一棵非负权树,求出每个结点与相距最远结点间的距离
先求出直径并找到夹住直径中点的两个点,然后分别对两个点的子树dfs,求出到这两个点的距离再加上另一侧直径的长度
这里有一份通过了某OJ该问题的代码
但是这个OJ数据比较水,能否有大佬帮我看一下这个做法是否正确,如果不正确能否给出一个Hack?
真的感谢喵~
#include <bits/stdc++.h>
using namespace std;
//自己平常用的奇怪码风,如果看不懂那真的很抱歉喵
class FD {
private:
inline static int Read() {
int r = 0, f = 0, c = getchar();
while((c < '0' || c > '9') && ~c) f |= c == '-', c = getchar();
while(c >= '0' && c <= '9') r = (r << 1) + (r << 3) + (c ^ 48), c = getchar();
return f ? -r : r;
}
typedef long long ll;
static constexpr int AwA = 1e4 + 10;
//链式前向星存树
struct Edge {
int nxt, v, w;
} e[AwA << 1];
int head[AwA], ecnt;
inline void AddEdge(int w, int u, int v) {
e[++ecnt] = { head[u], v, w }, head[u] = ecnt;
e[++ecnt] = { head[v], u, w }, head[v] = ecnt;
}
//这道题有多测
inline void Init() {
memset(head + 1, 0, sizeof(int) * n);
ecnt = 0;
}
//dfs求直径,同时算距离+求fa
void Dfs(int u, int _fa) {
fa[u] = _fa;
for(int i = head[u], v; i; i = e[i].nxt) {
v = e[i].v;
if(v == fa[u]) continue;
dis[v] = dis[u] + e[i].w;
Dfs(v, u);
}
if(dis[u] > dis[tar]) tar = u;
}
//对于子树内进行答案计算
void GetAns(int u, int _fa) {
fa[u] = _fa;
for(int i = head[u], v; i; i = e[i].nxt) {
v = e[i].v;
if(v == fa[u]) continue;
dis[v] = dis[u] + e[i].w;
GetAns(v, u);
}
ans[u] = dis[u] + len;
}
//n为树上节点数,m为直径节点数,tar为dfs求直径的辅助变量
int n, m, tar;
int fa[AwA];
//存直径上的节点
int sq[AwA];
ll dis[AwA], ans[AwA];
//直径另一半的长度
ll len;
public:
inline void Main() {
while((n = Read())) {
Init();
//压行特有的倒序Read
for(int i = 2; i <= n; i++) AddEdge(Read(), Read(), i);
//求直径
tar = 0;
dis[1] = 0;
Dfs(1, 0);
dis[tar] = 0;
Dfs(tar, 0);
//找出直径节点
m = 0;
for(int x = tar; x; x = fa[x]) sq[++m] = x;
//找到直径中点两侧的点,为sq[k]和sq[k-1]
int k = 0;
for(int i = m; i && !k; i--)
if(dis[sq[i]] * 2 <= dis[tar] && dis[sq[i - 1]] * 2 >= dis[tar]) k = i;
//对两个点包含子树分讨
len = dis[tar] - dis[sq[k]];
dis[sq[k]] = 0;
//不要让这个点走到另一个点的子树上
GetAns(sq[k], sq[k - 1]);
//另一个点同理
len = dis[sq[k - 1]];
dis[sq[k - 1]] = 0;
GetAns(sq[k - 1], sq[k]);
for(int i = 1; i <= n; i++) printf("%lld\n", ans[i]);
}
}
} Fd;
int main() {
Fd.Main();
return 0;
}