# 一、树的直径

• 若有多条直径，则所有的直径之间皆有公共点。

• 直径的两端一定是叶子。

• 树中距离某一直径端点最远的点，至少有一个是该直径的另一个端点。

• 对于树上任意一个点，与之距离最远的一个点，至少有一个直径的端点。

## 树形DP

O ( n ) O(n)

const int N = 50007, M = 500007, INF = 0x3f3f3f3f;

int n, m, F[N], D[N], ans ;
int ver[N], nex[M], head[N], edge[N], tot;
bool vis[N];
void add(int x, int y, int z)
{

ver[tot] = y;
edge[tot] = z;
}

void dp(int x){

vis[x] = 1;
for(int i = head[x] ;~i; i =nex[i]){

int y = ver[i], z = edge[i];
if(vis[y])continue;
dp(y);
ans = max(ans, D[x] + D[y] + z);
D[x] = max(D[x], D[y] + z);
}
}

int main(){

scanf("%d", &n);
for(int i = 1; i < n; ++ i){

int x, y;
scanf("%d%d", &x, &y);
}
dp(1);
printf("%d\n", ans);
return 0;
}



## 两次DFS / BFS（找到直径的两个端点）

O ( n ) O(n)

//两次dfs一次求P一次求Q
void dfs(int u,int &ed){

if(dis[u] > ans)ans = dis[u],ed = u;
vis[u] = 1;
for(int i = head[u];~i;i = nex[i]){

int v = ver[i],w = edge[i];
if(vis[v])continue;
dis[v] = dis[u] + w;
dfs(v,ed);
}
return ;
}
int p,q;
void solve(){

dfs(1,p);
ans = dis[p] = 0;
memset(vis,0,sizeof vis);
dfs(p,q);
cout << ans << endl;
}


typedef long long ll;
const int N = 500007, M = 5000007, INF = 0x3f3f3f3f;

int n, m;
ll d[N], copy_d[N];
bool vis[N];
int head[N], ver[M], nex[M], edge[M], tot;

void add(int x, int y, int z)
{

ver[tot] = y;
edge[tot] = z;
}

int bfs(int S)
{

queue<int>q;
memset(d, 0, sizeof d);
memset(vis, 0, sizeof vis);
//while(q.size())q.pop();
q.push(S), vis[S] = 1;

int maxx = 0, maxid;
while(q.size()){

int x = q.front();
q.pop();
for(int i = head[x]; ~i; i = nex[i]){

int y = ver[i], z = edge[i];
if(vis[y])continue;
vis[y] = 1;
d[y] = d[x] + z;
q.push(y);
if(d[y] > maxx){

maxx = d[y];
maxid = y;
}
}
}
return maxid;
}

int main()
{

scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++ i){

int x, y, z;
scanf("%d%d%d", &x, &y, &z);
}
int p, q;
p = bfs(1);
q = bfs(p);
ll ans = d[q];//the length of AB
for(int i = 1; i <= n; ++ i){

copy_d[i] = d[i];
}
bfs(q);
ll res = 0;
for(int i = 1; i <= n; ++ i){

res = max(res, min(d[i], copy_d[i]));//find the longest edges among the remaining edges
}
ans += res;
printf("%lld\n", ans);
return 0;
}



# 二、动态修改树的边权并求每个时刻的直径（线段树）

/*lmx,rmx,lmi,rmi,ld,rd,lrd,ans前缀/后缀的最大/最小和，前缀右减左最大，

#include<bits/stdc++.h>
#define ls(a) ((a)<<1)
#define rs(a) ((a)<<1|1)
#define N 200001
#define int long long
using namespace std;
typedef long long ll;
const int LL=1<<19;
char ibuf[LL],*ie=ibuf+LL,*ip=ie-1;
inline char nc(void){
G;return *ip;}
//#define getchar nc

char opt;ll flag=1,res=0;
while((opt=getchar())<'0'||opt>'9')if(opt=='-')flag=-1;
while(opt>='0'&&opt<='9'){
res=(res<<3)+(res<<1)+opt-'0';opt=getchar();}
return res*flag;
}
int n,m,st[N],top,q;
struct edge{
int to;ll v;int x;};vector<edge>g[N];
ll w,sum[N<<2],lmx[N<<2],rmx[N<<2],lmi[N<<2],rmi[N<<2],ld[N<<2],rd[N<<2],lrd[N<<2],ans[N<<2],lastans,p[N][2];
void pushup(int x){

sum[x]=sum[ls(x)]+sum[rs(x)];
lmx[x]=max(lmx[ls(x)],sum[ls(x)]+lmx[rs(x)]);
rmx[x]=max(rmx[rs(x)],sum[rs(x)]+rmx[ls(x)]);
lmi[x]=min(lmi[ls(x)],sum[ls(x)]+lmi[rs(x)]);
rmi[x]=min(rmi[rs(x)],sum[rs(x)]+rmi[ls(x)]);
ld[x]=max(ld[ls(x)],max(ld[rs(x)]-sum[ls(x)],lrd[ls(x)]+lmx[rs(x)]));
rd[x]=max(rd[rs(x)],max(sum[rs(x)]+rd[ls(x)],lrd[rs(x)]-rmi[ls(x)]));
lrd[x]=max(lrd[ls(x)]+sum[rs(x)],lrd[rs(x)]-sum[ls(x)]);
ans[x]=max(max(ans[ls(x)],ans[rs(x)]),max(ld[rs(x)]-rmi[ls(x)],rd[ls(x)]+lmx[rs(x)]));
}
inline void Build(int pos,int l,int r){

ll v;if(l==r)return sum[pos]=(v=st[l]),lmx[pos]=rmx[pos]=max(v,0ll),lmi[pos]=rmi[pos]=min(v,0ll),ld[pos]=rd[pos]=lrd[pos]=ans[pos]=v,void();
int mid=(l+r)>>1;
Build(ls(pos),l,mid),Build(rs(pos),mid+1,r);
pushup(pos);
}
inline void Change(int pos,int l,int r,int x,ll v){

if(l==r)return sum[pos]=v,lmx[pos]=rmx[pos]=max(v,0ll),lmi[pos]=rmi[pos]=min(v,0ll),ld[pos]=rd[pos]=lrd[pos]=ans[pos]=v,void();
int mid=(l+r)>>1;
(x<=mid)?Change(ls(pos),l,mid,x,v):Change(rs(pos),mid+1,r,x,v);
pushup(pos);
}
inline void dfs(int x,int prt){

int y;for(auto t:g[x])if((y=t.to)^prt)st[*p[t.x]=++top]=t.v,dfs(y,x),st[p[t.x][1]=++top]=-t.v;
}
signed main(void){

y,z,i}),g[y].push_back({
x,z,i});
dfs(1,0);Build(1,1,top);
while(q--){

Change(1,1,top,*p[x],y),Change(1,1,top,p[x][1],-y);
printf("%lld\n",lastans=ans[1]);
}
return 0;
}


# 三、树的重心

• 以树的重心为根时，所有子树的大小都不超过整棵树大小的一半。
• 把两棵树通过一条边相连得到一棵新的树，那么新的树的重心在连接原来两棵树的重心的路径上。
• 在一棵树上添加或删除一个叶子，那么它的重心最多只移动一条边的距离。
• 树中所有点到某个点的距离和中，到重心的距离和是最小的；如果有两个重心，那么到它们的距离和一样。
• 一棵树最多有两个重心，且相邻。

const int N = 50007, M = 500007, INF = 0x3f3f3f3f;
typedef long long ll;

int n, m;
ll T;
int son[N], ans, pos;
bool vis[N];

{

ver[tot] = y;
}

void dfs(int x)
{

vis[x] = 1, son[x] = 1;
int res = 0;
for(int i = head[x]; ~i; i = nex[i]){

int y = ver[i];
if(!vis[y]){

dfs(y);
son[x] += son[y];
res = max(res, son[y]);//x下面的子树
}
}
res = max(res, n - son[x]);//x上面的子树
if(res < ans || (res == ans && x < pos)){

ans = res, pos = x;
}
}

int main()
{

scanf("%lld", &T);
while(T -- ){

tot = 0;
memset(vis, 0, sizeof vis);
ans = INF;

scanf("%d", &n);
for(int i = 1; i < n; ++ i){

int x, y;
scanf("%d%d", &x, &y);
}
dfs(1);
printf("%d %d\n", pos, ans);
}
return 0;
}