树上直径

两种方法:

  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;
} 

 

posted @ 2020-08-14 21:25  wwwsfff  阅读(110)  评论(0编辑  收藏  举报