树的重心

定义

在一棵树中,若有一个点,除去它之后,树变成了两个连通块,这两个连通块的规模尽可能地接近,那么这个点就是这棵树的重心。
很显然,每棵树都有至少一个重心。

作用

树的重心可以把一棵无根树转化成有根树,同时让dfs的效率更高;保证了树的深度一定小于 n2 n 2

求树的重心

采用一遍dfs,计算每个点的相连的最大的连通块规模,求取最大值;再在所有最大值中找出最小的,这个点就是重心。
f[i] f [ i ] 代表第i个点相连的几个连通块中较大者的规模。
minn m i n n 代表 max{f[i]} m a x { f [ i ] }
size[i] s i z e [ i ] 代表第1个点为根的时候第 i i 个点子树的规模。

#include
using namespace std;
inline int read()
{
    int num=0;
    char c=' ';
    bool flag=true;
    for(;c>'9'||c<'0';c=getchar())
    if(c=='-')
    flag=false;
    for(;c>='0'&&c<='9';num=num*10+c-48,c=getchar());
    return flag ? num : -num;
}

    const int maxn=103000;
    int n;
    struct node
    {
        int y,next;
    }a[maxn*2];
    int head[maxn],top;
    //邻接表
    void add(int x,int y)
    {
        top++;
        a[top].y=y;
        a[top].next=head[x];
        head[x]=top;
    }
    //连一条从x到y的边
    void init()
    {
        n=read();
        for(int i=1;iint x=read();
            int y=read();
            add(x,y);
            add(y,x);
        }
    }
    //读入树的有关内容
    const int INF=2e9;
    int minn=INF;
    int f[maxn],size[maxn];
    void dfs(int u,int fa)
    {
        size[u]=1;//默认就只包含自己
        for(int i=head[u];i;i=a[i].next)
        {
            int v=a[i].y;
            if(v==fa)continue;
            dfs(v,u);
            size[u]+=size[v];
            f[u]=max(f[u],size[v]);
            //找出u所有子树中规模最大的
        }
        f[u]=max(f[u],n-size[u]);
        //规模n-size[u]是除去v的子树和u本身剩下的部分
        //也就是另一个连通块
        minn=min(minn,f[u]);
    }

int main()
{
    init();
    dfs(1,0);
    for(int i=1;i<=n;i++)
        if(f[i]==minn)
            printf("%d ",i);
    return 0;
}

你可能感兴趣的:(题解,树上问题)