给你一颗以1为根,2^5的树。让你求出以每个点为根的子树中,到子树中每个点距离和最小的点。
首先先做条件转化,到子树中每个点距离和最小,等价于重心,所以问题变成了求每棵子树的重心。
我们考虑如果用子树重心求出父亲的重心。发现一个显而易见的结论,父亲子树的重心,一定在重儿子子树的重心到根的连线上。
我们先做dfs,求出siz和重儿子。然后再做一遍dfs,每次从重儿子的重心往上爬,爬到再爬一步,最大子树会变大为止。这时看下取当前点和其重儿子答案是否相同,如果相同则重心有两个,为当前点和重儿子。否则重心只有一个,为当前点。
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 const int MAXN = 210000; 5 int n,cnt; 6 int head[MAXN],siz[MAXN],fa[MAXN],son[MAXN],ans[MAXN][2],to[2 * MAXN],nxt[2 * MAXN]; 7 void dfs1(int x) 8 { 9 siz[x] = 1; 10 for (int i = head[x];i;i = nxt[i]) 11 { 12 if (to[i] == fa[x]) 13 continue; 14 fa[to[i]] = x; 15 dfs1(to[i]); 16 siz[x] += siz[to[i]]; 17 if (siz[to[i]] > siz[son[x]]) 18 son[x] = to[i]; 19 } 20 } 21 void dfs2(int x) 22 { 23 for (int i = head[x];i;i = nxt[i]) 24 { 25 if (to[i] == fa[x]) 26 continue; 27 dfs2(to[i]); 28 } 29 int t = ans[son[x]][0]; 30 if (t == 0) 31 t = x; 32 while (t != x && max(siz[son[t]],siz[x] - siz[t]) >= max(siz[son[fa[t]]],siz[x] - siz[fa[t]])) 33 t = fa[t]; 34 ans[x][0] = t; 35 if (max(siz[son[t]],siz[x] - siz[t]) == max(siz[son[son[t]]],siz[x] - siz[son[t]])) 36 ans[x][1] = son[t]; 37 } 38 void add(int x,int y) 39 { 40 nxt[++cnt] = head[x]; 41 to[cnt] = y; 42 head[x] = cnt; 43 } 44 int main() 45 { 46 scanf("%d",&n); 47 int tx,ty; 48 for (int i = 1;i <= n - 1;i++) 49 { 50 scanf("%d%d",&tx,&ty); 51 add(tx,ty); 52 add(ty,tx); 53 } 54 dfs1(1); 55 dfs2(1); 56 for (int i = 1;i <= n;i++) 57 { 58 if (ans[i][1] != 0 && ans[i][1] < ans[i][0]) 59 swap(ans[i][1],ans[i][0]); 60 printf("%d",ans[i][0]); 61 if (ans[i][1] != 0) 62 printf(" %d\n",ans[i][1]); 63 else 64 printf("\n"); 65 } 66 return 0; 67 }
原文地址:https://www.cnblogs.com/iat14/p/12252812.html
时间: 2024-10-31 19:13:30