浅谈树的重心

浅谈树的直径


定义:

  树上一节点最大子树的节点数最小;c++

性质:

  1.删除重心后所得的全部子树,节点数不超过原树的1/2,一棵树最多有两个重心;数组

  2.树中全部节点到重心的距离之和最小,若是有两个重心,那么他们距离之和相等;优化

  3.两个树经过一条边合并,新的重心在原树两个重心的路径上;spa

  4.树删除或添加一个叶子节点,重心最多只移动一条边;3d

求解:

  求解方法多种多样,分别用到不一样的定义和性质:
code

  1.定义求解:

  siz [ i ]表示 i 节点的子树大小 dp [ i ]表示以 i 为根节点的最大子树大小,val[ i ]为i节点的点权,代码通俗易懂不过多解释了blog

  

inline void dfs(int now,int fa) { siz[now]=val[now]; dp[now]=0; for(int i=head[now];i;i=a[i].nxt) { int t=a[i].to; if(t==fa) continue; dfs(t,now); siz[now]+=siz[t]; dp[now]=max(dp[now],siz[t]); } dp[now]=max(dp[now],n-dp[now]); if(dp[now]<dp[ans]) ans=now; }

  2.性质求解:

  通常来讲定义求解就够用了,但在某些时候性质求解更方便实用;get

  根据性质2:咱们能够处理出全部节点到某一节点的距离,取最小值;it

  怎么求出每一个节点到某一节点的距离呢?在dfs过程当中向下处理是很容易的,因此咱们能够先处理出全部节点到根节点的距离qwq ;io

  siz [ i ]同上,f [ i ] 表示节点 i 的全部子节点到 i 的距离和,val[ i ] 同上, a [ i ].val 为边权,设定1 号节点为根节点;

  

inline void dfs1(int now,int fa,int deep) { siz[now]=val[now]; dep[now]=deep; for(int i=head[now];i;i=a[i].nxt) { int t=a[i].to; if(t==fa) continue; dfs1(t,now,deep+a[i].val); siz[now]+=siz[t]; f[now]+=f[t]+siz[t]*a[i].val; } }

 

  对于f数组的处理的理解:t的全部子节点到t的距离+now的当前子树全部节点到now的距离。

  这样就求得了根节点的距离和,咱们再经过根节点递推其余节点的距离和,有以下公示:

  f [ now ] = f [ fa ] +( siz [ 根节点 ] - 2 * siz [ now ])* 边权;(now!= 根节点)

 

  理解以下:

  对于now的子节点,每一个节点的距离减小了一个边权,总距离减小 siz [ now ] * 边权 ,对于非v子节点,每一个节点距离增长了一个边权,总距离增长(siz[ 根 ]-siz [ now ])*边权

  

inline void dfs2(int now,int fa) { if(now^root) f[now]=f[fa]+siz[1]-2*siz[now]; if(f[now]<sum) res=now,sum=f[now]; for(int i=head[now];i;i=a[i].nxt) { int t=a[i].to; if(t==fa) continue; dfs2(t,now); } }

  这种方法还能够优化:观察式子:显然一个节点的全部子树中,只有子节点数最多的一个可能成为重心,因此咱们能够加以改进,在dfs2中只走子树节点最多的一个:

  这样复杂度总体虽然仍是O(n)的,可是查询复杂度变为了O(树高)在某些题目中(下面例题中qwq)有奇效。

 

inline void dfs1(int now,int fa,int deep) { siz[now]=val[now]; dep[now]=deep; int maxson=-1;//新 加 
    for(int i=head[now];i;i=a[i].nxt) { int t=a[i].to; if(t==fa) continue; dfs1(t,now,deep+a[i].val); siz[now]+=siz[t]; f[now]+=f[t]+siz[t]*a[i].val; if(siz[t]>maxson) maxson=siz[t],son[now]=t;//新 加 
 } } inline void dfs2(int now,int fa) { if(now^1) f[now]=f[fa]+siz[1]-2*siz[now]; if(f[now]<sum) res=now,sum=f[now]; if(son[now]) dfs2(t,now);//改 动 
}

例题: 

  洛谷P2726

  

  第一行为N,1<N<=50000,表示树的节点数目,树的节点从1到N编号。 接下来N-1行,每行两个整数U,V,表示U与V之间有一条边。 再接下N行,每行一个正整数,其中第i行的正整数表示编号为i的节点权值为W(I),树的深度<=100

  分析:

  应该没有黑题难度,紫色差很少。

  先考虑暴力枚举x,y,那么对于每一对x,y分界都是一条树上的边。那么咱们不如枚举断边,再找出重心qwq;

  先O(n)求出f [ root ] 的值,枚举断边,再经过上述第二种优化过的方法求距离和,总复杂度O(N*树高);

  对于优化的处理:因为须要断边,每次断边后最大子树可能变小,因此咱们须要维护一个次大子树;

  

#include<bits/stdc++.h>
using namespace std; #define int long long inline int read() { int x=0,f=1; char ch; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if(ch=='-') f=0,ch=getchar(); while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return f?x:-x; } const int inf=1e18; int n,res,cut; int val[100010],siz[100010],f[100010],dep[100010]; int from[100010]; int son1[100010],son2[100010]; int head[100010],cnt=1; struct point { int nxt,to; }a[100010]; inline void add(int x,int y) { a[++cnt].nxt=head[x]; a[cnt].to=y; head[x]=cnt; } inline void dfs1(int now,int fa,int deep) { siz[now]=val[now]; dep[now]=deep; from[now]=fa; for(int i=head[now];i;i=a[i].nxt) { int t=a[i].to; if(t==fa) continue; dfs1(t,now,deep+1); siz[now]+=siz[t]; f[now]+=(f[t]+siz[t]); if(siz[t]>siz[son1[now]]) { son2[now]=son1[now]; son1[now]=t; } else if(siz[t]>siz[son2[now]]) { son2[now]=t; } } } inline void dfs3(int now,int sum,int all,int &ans) { ans=min(ans,sum); int t=son1[now]; if(t==cut||siz[son2[now]]>siz[son1[now]]) t=son2[now]; if(!t) return ; if(2*siz[t]>all) dfs3(t,sum+all-2*siz[t],all,ans); } inline void dfs2(int now) { for(int i=head[now];i;i=a[i].nxt) { int t=a[i].to; if(t==from[now]) continue; cut=t; for(int x=now;x;x=from[x]) siz[x]-=siz[t]; int A=inf,B=inf; dfs3(1,f[1]-f[t]-dep[t]*siz[t],siz[1],A); dfs3(t,f[t],siz[t],B); res=min(res,A+B); for(int x=now;x;x=from[x]) siz[x]+=siz[t]; dfs2(t); } } signed main() { n=read(); for(int x,y,i=1;i<n;++i) { x=read(),y=read(); add(x,y); add(y,x); } for(int i=1;i<=n;++i) { val[i]=read(); } res=inf; dfs1(1,0,0); dfs2(1); printf("%lld\n",res); return 0; }
相关文章
相关标签/搜索