树的重心
定义
对于树上的每一点,计算其所有子树(包括"向上"的那棵子树)中最大的子树节点数,这个值最小的点就是这棵树的重心。 ------OI WiKi
我的理解就是重心是整个物体(在这里是树)的平衡点,为了保持平衡从重心出发到其他点的距离都不会太大而导致失衡。
性质
1.一棵树最少有一个重心,最多有两个重心,若有两个重心,则它们有边相连。
2.树上所有点到某个点的距离和里,到重心的距离和最小,若有两个重心,则其距离和相同。
3.若以重心为根,则所有子树的大小都不超过整棵树的一半。
4.在一棵树上添加或删除一个叶子节点,其重心最多移动到相邻结点。
5.两棵树通过连一条边组合成新树,则新树重心在原来两棵树的重心的连线上。
求解重心
边点无权值
很普通的一棵树,按照定义来求即可。
任选一个节点作为根节点,跑DFS,记录所有子树的大小(包括向上的那一棵)。
这样就得到了每个节点最大子树的节点数,取最小的那个节点即可。
例题:Balancing Act
参考代码:
#include<iostream>
#include<cmath>
#include<cstring>
using namespace std;
const int N=2e4+10;
int ans[N];
int head[N],cnt;
struct {
int to,next;
}e[2*N];
int n;
void add(int a,int b){
e[++cnt].to=b;
e[cnt].next=head[a];
head[a]=cnt;
e[++cnt].to=a;
e[cnt].next=head[b];
head[b]=cnt;
}
int dfs(int u,int f){
//printf("%d ",u);
int num=0,temp;
int to;
for(int i=head[u];i;i=e[i].next){
to=e[i].to;
if(f==to)continue;
temp=dfs(to,u);
num+=temp;
ans[u]=max(ans[u],temp);
}
++num;
ans[u]=max(ans[u],n-num);
return num;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
cnt=0;
memset(ans,0,sizeof(ans));
memset(head,0,sizeof(head));
int u,v;
for(int i=1;i<n;++i){
scanf("%d%d",&u,&v);
add(u,v);
}
dfs(1,0);
int ind=1;
for(int i=1;i<=n;++i){
if(ans[i]<ans[ind])ind=i;
}
printf("%d %d\n",ind,ans[ind]);
}
return 0;
}
多测不清空,爆零两行泪
点有权值,边无权值
例题:H - Hoist the Tree 感觉这个链接随时可能会炸额
首先以1为根节点算一遍代价,然后就是代价的转移。相邻节点的代价是可以直接转移的。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
#define ll long long
int dep[N],w[N],head[N],cnt;
ll size[N];
struct{
int to,next;
}e[2*N];
void add(int u,int v){
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
e[++cnt].to=u;
e[cnt].next=head[v];
head[v]=cnt;
}
ll ans;
void dfs(int u,int f){
int to;
for(int i=head[u];i;i=e[i].next){
to=e[i].to;
if(to==f)continue;
dep[to]=dep[u]+1;
dfs(to,u);
size[u]+=size[to];
}
ans+=1ll*dep[u]*w[u];
size[u]+=w[u];
}
void work(int u,int f,ll pre){
int v;
ans=min(ans,pre);
for(int i=head[u];i;i=e[i].next){
v=e[i].to;
if(v==f)continue;
work(v,u,pre+size[1]-2*size[v]);//这里是转移方程,下面代价减掉(-size[v]),上面代价加上(+size[1]-size[v])
}
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)scanf("%d",&w[i]);
int u,v;
for(int i=1;i<n;++i)scanf("%d%d",&u,&v),add(u,v);
dfs(1,0);
work(1,0,ans);
// for(int i=1;i<=n;++i)printf("%d%c",size[i],i==n?'\n':' ');
//for(int i=1;i<=n;++i)printf("%d%c",dep[i],i==n?'\n':' ');
printf("%lld",ans);
return 0;
}