1树的重心
树的重心,也叫树的质心。对于一棵树来说,删去该树的重心后,所有的子树的大小不会超过原树大小的二分之一。树的重心还有一个性质,是相对于树上的其他点而言的,就是删去重心后形成的所有子树中最大的一棵节点数最少。换句话说,就是删去重心后生成的多棵子树是最平衡的。一棵树的重心至多有两个。
两种处理方法
- 找最大子树siz最小的节点为重心
- 找最大子树<=n/2的节点为重心(好写)
POJ1655
求字典序最小的重心及其最大子树的size
const int N=20100;
const int M=40100;
int head[N],cnt;
struct node
{
int nxt,v;
}edge[M];
void add(int u,int v)
{
cnt++;
edge[cnt].v=v;
edge[cnt].nxt=head[u];
head[u]=cnt;
}
int siz[N];
int root,min_maxss,n;
void init()
{
cnt=0;min_maxss=inf;
memset(head,0,sizeof(head));
memset(siz,0,sizeof(siz));
}
void dfs(int u,int fa)
{
siz[u]=1;int maxss=0;
for(int i=head[u];i;i=edge[i].nxt)
{
int v=edge[i].v;
if(v==fa) continue;
dfs(v,u);
siz[u]+=siz[v];
maxss=max(maxss,siz[v]);
}
maxss=max(maxss,n-siz[u]);
if(maxss<min_maxss||(maxss==min_maxss)&&(u<root))
{
root=u;
min_maxss=maxss;
}
}
int main()
{
int T=read();
int u,v;
while(T--)
{
init();n=read();
for(int i=1;i<=n-1;i++)
{
u=read();v=read();
add(u,v);add(v,u);
}
dfs(1,0);
cout<<root<<' '<<min_maxss<<endl;
}
return 0;
}
树的直径
算法1:我们任取树中的一个节点x,找出距离它最远的点y,那么点y就是这棵树中一条直径的一个端点。我们再从y出发,找出距离y最远的点就找到了一条直径。这个算法依赖于一个性质:对于树中的任一个点,距离它最远的点一定是树上一条直径的一个端点。
算法2:首先,先将无根树转成有根树,定义F[i]表示从i出发向远离根节点的方向走的最长路径的长度,G[i]表示从i向远离根节点的方向走的次长路径的长度。注意F[i]和G[i]不能沿着i的同一个儿子走。特别地,如果i只有一个儿子,那么G[i]=0。答案为max(F[i]+G[i])。
void DFS(int x){
f[x]=g[x]=0;
for(int i=0;i<G[x].size();i++){
Node v=G[x][i];
if (!vist[v.to]){
vist[v.to]=true;
DFS(v.to);//访问子节点。
vist[v.to]=false;
if (f[x]<f[v.to]+v.val){//如果发现了一条更长的路径,那么更新f[x]和g[x]。
g[x]=f[x];//原来的f[x]变为次长路,新发现的记为最长路。
f[x]=f[v.to]+v.val;
}
else if (g[x]<f[v.to]+v.val) g[x]=f[v.to]+v.val;
//如果找到了一条比次长路更长的路径,那么更新g[x]。
}
}
ans=max(ans,f[x]+g[x]);
}