P3174 [HAOI2009]毛毛虫
两种方法:
1、记录以u为根的子树中最深d1和次深d2,然后求出max{d1+d2-1}即可
pf:最长链必然存在一个最高点,最高点必取他的最深和次深(反证法)
2、两遍DFS:先从任意一点P出发,找离它最远的点Q,再从点Q出发,找离它最远的点W,W到Q的距离就是是的直径 /byhttps://www.cnblogs.com/handsome-zyc/p/11237529.html
pf: 1、若P已经在直径上,根据树的直径的定义可知Q也在直径上且为直径的一个端点
2、若P不在直径上,假设此时WQ不是直径,AB是直径
(1)若AB与PQ有交点C,由于P到Q最远,那么PC+CQ>PC+CA,所以CQ>CA,易得CQ+CB>CA+CB,即CQ+CB>AB,与AB是直径矛盾,不成立,如下图(其中AB,PQ不一定是
直线,画成直线是为了方便):
(2)若AB与PQ没有交点,M为AB上任意一点,N为PQ上任意一点。首先还是NP+NQ>NQ+MN+MB,同时减掉NQ,得NP>MN+MB,易知NP+MN>MB,所NP+MN+MA>MB+MA,
即NP+MN+MA>AB,与AB是直径矛盾,所以这种情况也不成立
练习:
1、P3174 [HAOI2009]毛毛虫
把儿子数量当做权值,求树的直径
用第一种方法:合并需要考虑是否为根节点,(坑)如果有父亲,需要多算一个父亲,否则d1[u]+d2[u]-deg[u]+1
#include<bits/stdc++.h> using namespace std; const int N=1e6+5; int n,m,d1[N],d2[N],deg[N],ans; int cnt,to[N],nxt[N],he[N]; inline void add(int u,int v) { to[++cnt]=v,nxt[cnt]=he[u],he[u]=cnt; } void dfs(int fa,int u) { deg[u]=0; for(int e=he[u];e;e=nxt[e]) { int v=to[e]; if(v!=fa){ dfs(u,v); deg[u]++; } } } void dgs(int fa,int u) { for(int e=he[u];e;e=nxt[e]) { int v=to[e]; if(v!=fa) { dgs(u,v); if(d1[v]>d1[u]) { d2[u]=d1[u],d1[u]=d1[v]; } else d2[u]=max(d2[u],d1[v]); } } d1[u]+=deg[u],d2[u]+=deg[u]; // if(u==8) printf("ll %d %d\n",d1[u],d2[u]); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v),add(v,u); } dfs(0,1); dgs(0,1); ans=d1[1]+d2[1]-deg[1]+1; for(int i=2;i<=n;i++) { ans=max(ans,d1[i]+d2[i]-deg[i]+2); // printf("%d %d %d\n",d1[i],d2[i],deg[i]); } printf("%d\n",ans); return 0; }