1. 树的重心:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
性质:
- 树中所有点到重心的距离和是最小的,一棵树最多有两个重心(比如就两个点连个边).
- 把两棵树通过加一条边得到一颗新的树,新的树的重心必然在连接原来两棵树重心的路径上.
- 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置.
树的重心的两种求法:
- 按定义求:跑一遍dfs.
1 #include <iostream> 2 using namespace std; 3 const int N = 2e5+10; 4 int ans = 0x3f3f3f,h[N],e[N],ne[N],idx,st[N],n;//邻接表存储树 5 void add(int a,int b)//不管是a插入b还是b插入a都是idx++,邻接表的idx应该是指边的编号 6 {//e[idx] 是以idx为起点,该起点的值,ne[idx]是该边的终点 所以e存在值相同的点 7 ne[idx] = h[a],h[a] = idx,e[idx++] = b; 8 } 9 int dfs(int u)//返回连通块大小 10 { 11 st[u] = 1; 12 int sum = 1,res = 0; 13 for(int i=h[u];i!=-1;i=ne[i]) 14 { 15 int j = e[i];//e[i]存储的实际是该起点在邻接表数组的位置 16 if(!st[j]) 17 { 18 int s = dfs(j); 19 res = max(res,s);//res是不算上u结点的子连通块最大的结点数 20 sum+=s; 21 } 22 } 23 ans = min(max(res,n-sum),ans); 24 return sum; 25 } 26 int main() 27 { 28 fill(h,h+N,-1); 29 scanf("%d",&n); 30 for(int i=0;i<n-1;i++){ 31 int x,y; scanf("%d%d",&x,&y); 32 add(x,y),add(y,x); 33 } 34 dfs(1); 35 printf("%d\n",ans); 36 return 0; 37 }
- 树形dp:换根dp 枚举点,求其他点到该点的距离,取最小值.
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 typedef long long LL; 5 const int N = 100010; 6 int h[N],w[N],n,idx,m; 7 LL f[N],sum[N]; 8 struct Road{ 9 int to,ne,w; 10 }road[N<<1]; 11 void add(int a,int b,int c) 12 { 13 road[idx].w= c,road[idx].to = b,road[idx].ne = h[a],h[a] = idx++; 14 } 15 void dfs_down(int u,int fa) 16 { 17 sum[u] = w[u]; 18 for(int i=h[u];i!=-1;i=road[i].ne) 19 { 20 int v = road[i].to; 21 if(v==fa) continue; 22 dfs_down(v,u); 23 f[u] += f[v]+sum[v]*road[i].w; 24 sum[u]+=sum[v]; 25 } 26 } 27 void dfs_up(int u,int fa) 28 { 29 for(int i=h[u];i!=-1;i=road[i].ne) 30 { 31 int v = road[i].to; 32 if(v==fa) continue; 33 f[v] = road[i].w*(m-2*sum[v])+f[u]; 34 dfs_up(v,u); 35 } 36 } 37 int main() 38 { 39 memset(h,-1,sizeof h); 40 scanf("%d",&n); 41 for(int i=1;i<=n;i++) scanf("%d",&w[i]),m+=w[i]; 42 for(int i=1;i<n;i++) 43 { 44 int a,b,c; scanf("%d%d%d",&a,&b,&c); 45 add(a,b,c); add(b,a,c); 46 } 47 dfs_down(1,-1); 48 dfs_up(1,-1); 49 LL ans = 1e14; 50 for(int i=1;i<=n;i++) ans = min(ans,f[i]); 51 printf("%lld\n",ans); 52 return 0; 53 }
2. 树的中心:一个结点,对于每一个点的距离的最大值最小.
树的中心的求法:
- 树形dp(树的直径):
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 const int N = 10010; 5 int h[N],idx,n,d1[N],d2[N],by[N],up[N]; 6 struct Road{ 7 int fr,to,ne,w; 8 }road[N<<1]; 9 void add(int a,int b,int c) 10 { 11 road[idx].w = c,road[idx].fr = a,road[idx].to = b,road[idx].ne =h[a],h[a]= idx++; 12 } 13 int dfs(int u,int fa) 14 { 15 for(int i=h[u];i!=-1;i=road[i].ne) 16 { 17 int v = road[i].to; 18 if(v==fa) continue; 19 int dist = dfs(v,u)+road[i].w; 20 if(dist>=d1[u]) d2[u] = d1[u],d1[u] = dist,by[u] = v; 21 else if(dist>d2[u]) d2[u] = dist; 22 } 23 return d1[u]; 24 } 25 void dfs_up(int u,int fa) 26 { 27 for(int i=h[u];i!=-1;i=road[i].ne) 28 { 29 int v = road[i].to; 30 if(v==fa) continue; 31 if(by[u]==v) up[v] = max(d2[u],up[u])+road[i].w; 32 else up[v] = max(d1[u],up[u])+road[i].w; 33 dfs_up(v,u); 34 } 35 } 36 int main() 37 { 38 scanf("%d",&n); 39 memset(h,-1,sizeof h); 40 for(int i=1;i<n;i++) 41 { 42 int a,b,c; scanf("%d%d%d",&a,&b,&c); 43 add(a,b,c); add(b,a,c); 44 } 45 dfs(1,-1); 46 dfs_up(1,-1); 47 int ans = 0x3f3f3f3f; 48 for(int i=1;i<=n;i++) 49 ans = min(max(d1[i],up[i]),ans); 50 printf("%d\n",ans); 51 return 0; 52 }