最近学了下点分治
说道点分治就得先说到树的重心
树的重心的定义是:最大的子树最小的节点。
为什么要找树的重心呢
因为找到树的重心把他变成根以后,最大的子树的大小不超过n/2,否则如果超过n/2将该子树的根作为重心将会更优。
这样可以保证递归的层数不超过logn层,同时保证每个点最多被计算logn次。
那么如何找重心呢
根据定义,我们不妨维护一下两个数组sz[]和mx[]表示以r为根的子树大小和无根树中的最大子树大小,那么mx最小的那个节点就是重心。
有两种写法 一种是
void getroot(int x,int fa) { son[x]=1;f[x]=0; for(int i=head[x];i;i=e[i].next) { if(e[i].to==fa||vis[e[i].to])continue; getroot(e[i].to,x); son[x]+=son[e[i].to]; f[x]=max(f[x],son[e[i].to]); } f[x]=max(f[x],sum-son[x]); if(f[x]<f[root])root=x; }
另一种写法是用两个函数
void dfs_size(int x,int fa) { sz[x]=1; mx[x]=0; for(Edge*p=fir[x];p;p=p->next) { if(p->to==fa || vis[p->to]==clk) continue; dfs_size(p->to,x); sz[x] += sz[p->to]; maxit(mx[x],sz[p->to]); } } int root=0; void dfs_root(int r,int x,int fa) { maxit(mx[x],sz[r]-sz[x]); if(mx[x]<mx[root]) root=x; for(Edge*p=fir[x];p;p=p->next) { if(p->to==fa || vis[p->to]==clk) continue; dfs_root(r,p->to,x); } }
我采用的后者,因为太弱了第一种一开始没看懂。
我一直觉得只用dfs一次就可以把每个子树的重心找到,
时间: 2024-11-07 20:38:35