zoukankan      html  css  js  c++  java
  • 树的直径与重心

    树的直径与重心

    直径

    定义

    树中所有最短路径距离的最大值即为树的直径。

    性质

    1. 直径两端点一定是两个叶子节点

    2. 距离任意点最远的点一定是直径的一个端点,这个基于贪心求直径方法的正确性可以得出

    3. 对于两棵树,如果第一棵树直径两端点为(u,v),第二棵树直径两端点为(x,y),用一条边将两棵树连接,那么新树的直径一定是u,v,x,y中的两个点.证明:如果新树直径不是原来两棵树中一棵的直径,那么新直径一定经过两棵树的连接边,新直径在原来每棵树中的部分一定是距离连接点最远的点,即一定是原树直径的一个端点。

    4. 对于一棵树,如果在一个点的上接一个叶子节点,那么最多会改变直径的一个端点.证明:假设在xx下面接一个点yy,直径变成了(u,x),原树直径为(a,b),那么(dis(u,x)>dis(a,b),dis(u,x)=dis(u,y)+1),即(dis(u,y)+1>dis(a,b)),如果(dis(u,y) < dis(a,b))
      那么显然不成立;如果(dis(u,y)=dis(a,b)),那么((u,y))也是原树的直径,符合上述结论。

    5. 若一棵树存在多条直径,那么这些直径交于一点且交点是这些直径的中点

    算法

    两边dfs或者一遍树形dp

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define lson x<<1
    #define rson x<<1|1
    #define ll long long
    #define rint register int
    #define mp(x,y) make_pair(x,y)
    using namespace std;
    template<typename xxx>void read(xxx &x)
    {
        x=0;int f=1;char c=getchar();
        for(;c<'0'||c>'9';c=getchar()) if(c=='-') f=-1;
        for(;c>='0'&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
        x*=f;
    }
    template<typename xxx>void print(xxx x)
    {
        if(x<0){putchar('-');x=-x;}
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    const int maxn=200020;
    const int inf=0x7fffffff;
    struct edge{
        int last,val,to;
    }e[maxn];
    int head[maxn],tot;
    inline void add(int from,int to,int val)
    {
        tot++;
        e[tot].to=to;
        e[tot].val=val;
        e[tot].last=head[from];
        head[from]=tot;
    }
    int vis[maxn];//整棵树的直径就是max{f[x]}(1 <= x <= n)
    int d[maxn];//表示从节点x出发走向以x为根的子树,能够到达的最远节点的距离,d[x] = max{d[yi] + edge(x, yi)}(1 <= i <= t)
    //int f[maxn];//经过节点x的最长链的长度"f[x],f[x] = max{d[yi] + d[yj] + edge(x, yi) + edge(x, yj)}(1 <= j < i <= t),可被ans代替 
    int ans,n,m;
    inline void dp(int x)
    {
        vis[x]=1;
        for(rint i=head[x];i;i=e[i].last)
        {
            if(vis[e[i].to]) continue;
            dp(e[i].to);
            ans=max(ans,d[x]+d[e[i].to]+e[i].val);
            d[x]=max(d[x],d[e[i].to]+e[i].val); 
        }
    }
    int main()
    {
        read(n);read(m);
        for(rint i=1;i<=m;i++)
        {
            int a,b,c;char s[2];
            read(a);read(b);read(c);scanf("%s",s);
            add(a,b,c);add(b,a,c);
        }
        dp(1);
        print(ans);
    }
    /*
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define lson x<<1
    #define rson x<<1|1
    #define ll long long
    #define rint register int
    #define mp(x,y) make_pair(x,y)
    using namespace std;
    template<typename xxx>void read(xxx &x)
    {
        x=0;int f=1;char c=getchar();
        for(;c<'0'||c>'9';c=getchar()) if(c=='-') f=-1;
        for(;c>='0'&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
        x*=f;
    }
    template<typename xxx>void print(xxx x)
    {
        if(x<0){putchar('-');x=-x;}
        if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    const int maxn=200020;
    const int inf=0x7fffffff;
    struct edge{
        int last,val,to;
    }e[maxn];
    int head[maxn],tot;
    inline void add(int from,int to,int val)
    {
        tot++;
        e[tot].to=to;
        e[tot].val=val;
        e[tot].last=head[from];
        head[from]=tot;
    }
    int vis[maxn];
    int d[maxn],tmp,max_num;
    int ans,n,m;
    queue<int>q;
    inline void bfs(int x)
    {
        ans=0;
        memset(vis,0,sizeof(vis));
        memset(d,0,sizeof(d));
        vis[x]=1;q.push(x);
        while(q.size())
        {
            int x=q.front();q.pop();
            for(rint i=head[x];i;i=e[i].last)
            {
                if(!vis[e[i].to])
                {
                    vis[e[i].to]=1;
                    d[e[i].to]=d[x]+e[i].val;
                    if(ans<d[e[i].to]) 
                    {
                        ans=d[e[i].to];
                        tmp=e[i].to;
                    }
                    q.push(e[i].to);
                }
            }
        }
    }  
    inline void dfs(int x,int len)
    {
        vis[x]=1;
        if(len>ans) ans=len,tmp=x;
        for(rint i=head[x];i;i=e[i].last)
            if(!vis[e[i].to]) dfs(e[i].to,len+e[i].val);
    } 
    int main()
    {
        read(n);read(m);
        for(rint i=1;i<=m;i++)
        {
            int a,b,c;char s[2];
            read(a);read(b);read(c);scanf("%s",s);
            add(a,b,c);add(b,a,c);
        }
        dfs(1,0);
        max_num=tmp;
        memset(vis,0,sizeof(vis));
        dfs(max_num,0);
        print(ans);
    }
    //两遍bfs或dfs 
    */
    

    重心

    定义

    最大子树最小的节点

    性质

    性质1:“树中所有点到某个点的距离和中,到重心的距离和是最小的
    性质2:把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
    性质3: 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
    性质4: 一棵树最多有两个重心,且相邻。

    算法

    每次找到一个节点的最大子树更新ans,一个节点的子树包括指向的子树与减去当前节点子树后剩下的部分。

    例题

    Luogu-P1395

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <queue>
    #include <map>
    #include <cstring>
    #include <algorithm>
    #define rint register int
    #define ll long long
    using namespace std;
    template <typename xxx> inline void read(xxx &x)
    {
    	int f = 1;x = 0;
    	char c = getchar();
    	for(; c < '0' || c > '9' ; c = getchar()) if(c=='-') f = -1;
    	for(;'0' <= c && c <= '9'; c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
    	x *= f;
    }
    template <typename xxx> inline void print(xxx x)
    {
    	if(x < 0) {
    		putchar('-');
    		x = -x;
    	}
    	if(x > 9) print(x/10);
    	putchar(x % 10 + '0');
    }
    const int inf = 0x7fffffff;
    const int maxn = 100200;
    const int mod = 2015;
    struct edge{
    	int to,last;
    }e[maxn];
    int head[maxn],tot;
    inline void add(int from,int to) {
    	++tot;
    	e[tot].to = to;
    	e[tot].last = head[from];
    	head[from] = tot;
    }	
    int n,ans = inf,rt = 1;
    int siz[maxn],dis[maxn];
    inline void ddfs(int x,int fa){
    	siz[x] = 1;int ret = 0;
    	for(rint i = head[x]; i; i = e[i].last) {
    		if(e[i].to == fa) continue;
    		ddfs(e[i].to,x);
    		siz[x] += siz[e[i].to];
    		ret = max(ret,siz[e[i].to]);
    	}
    	ret = max(ret,n - siz[x]);
    	if(ret < ans) {
    		ans = ret;
    		rt = x;
    	}
    	else if(ret == ans && rt > x) rt = x;
    	return ;
    }
    inline void fk(int x,int fa) {
    	dis[x] = dis[fa] + 1;
    	for(rint i = head[x];i;i = e[i].last) {
    		if(e[i].to == fa) continue;
    		fk(e[i].to,x);
    	}
    }
    int main()
    {
    	read(n);
    	for(rint i = 2;i <= n; ++i) {
    		int a,b;
    		read(a);read(b);
    		add(a,b);add(b,a);
    	}
    	dis[0] = -1;
    	ddfs(1,0);fk(rt,0);
    	int tem = 0;
    	for(rint i = 1;i <= n; ++i) tem += dis[i];
    	print(rt);putchar(' ');print(tem);
    	return 0;
    }
    
    
  • 相关阅读:
    unix domain socket 浅析
    Python单元测试的Mock是怎么回事
    三招搞定你的ubuntu安全问题
    思考一次整体调整Python项目规范性的过程
    不可缺少的程序埋点
    python + unittest + request + parameterized 参数化遇到中文名称testcase不显示的问题
    【CDH】cdh搭建遇到的坑和解决过程
    [Linux系统]安装时出现Requires: libc.so.6(GLIBC_2.17)(64bit) Requires: systemd Requires: libstdc++.so时解决办法
    【Linux命令】在Linux服务器上与windows通过SCP命令互传文件时出现的问题排查过程
    【微信公众号】记一次微信活动微信公众号分享没有LOGO的解决心路历程
  • 原文地址:https://www.cnblogs.com/Thomastine/p/11746098.html
Copyright © 2011-2022 走看看