树分治和LCA

树分治

 在树中将树分成几个部分,然后依次解决的方法,这就是一种优化的方法。那么要如何去分治一棵树呢,这里需要一个概念,那就是树的质心
那么什么是树的质心呢
 指的是让这棵树中的一个点为根,使得这棵树的最大子树的节点数最少
这就是树的质心。
 那么要怎么找出一个树的质心呢。
    
 假设是这样的一棵树,可见的这棵树含有三个子树,要寻找这棵树的最大子树,那么久需要比较以这颗树中的任意一个节点为根,比较所有的子树的大小,记录最大子树中最小的一个。那么我们要做的就是如何使得能够找出以任意一个节点为根节点的最大子树。
  那么就需要遍历所有的节点,这里首先需要一个存图的数据结构,那么存图又有两种形式,一种是邻接表,一种是邻接矩阵。
这里的话我们使用邻接表
void add(int u,int v)
{
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
这是邻接表的写法,每次传入两个参数,这里的两个参数是指一条边的两个点。将这条边的终点存放在数组edge[i].to中,然后这个数组是一个结构体数组这样的数组还含有另一个参数,而这个参数存储的是这条边是以u为起点的第几条边。
  
 接下来就是让子树1和这棵树剩下的其他子树的和一起比较,比如子树1和子树2和3一起形成的子树比较。这样的比较就是为了排除一种极端的情况,就是当子树1十分的大的时候,比2和3加起来还要大。比较完了之后,

 然后是子树2和子树1比较大小,然后就是上面的子树2和子树1和3比较。
最后就是2和3 比较。这样一次次的递归下来就能找出最大子树的最小节点数的子树。
 
#include<cstdio>
#include<cstring>
#include<iostream>
 
using namespace std;
 
struct node
{
    int to;
    int next;
}edge[40005];
 
 
int son[40005],head[40005];
int cnt=0,size=0x3f3f3f3f,n,ans;
int vis[40005];
 
void add(int u,int v)
{
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
 
void init()
{
    cnt=0;
    size=1<<30;
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
}
 
int max(int a,int b)
{
    if(a<b)
    {
        return b;
    }
    else
    {
        return a;
    }
}
 
int DFS(int u)
{
    vis[u]=1;
    son[u]=0;
    int tmp=0;
    for(int i=head[u];~i;i=edge[i].next)
    {
        int x=edge[i].to;
        if(!vis[x])
        {
            DFS(x);
            son[u]+=son[x]+1;
            tmp=max(tmp,son[x]+1);
        }
    }
    tmp=max(tmp,n-son[u]-1);
    if(tmp<size||tmp==size&&u<ans)
    {
        ans=u;
        size=tmp;
    }
}
 
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        init();
        cin>>n;
        int i,u,v;
        for(i=1;i<n;i++)
        {
           cin>>u>>v;
           add(u,v);
           add(v,u);
        }
        DFS(1);
        printf("%d %d ",ans,size);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/yewa/p/7243589.html