树的重心
性质:一个点是重心,等价于,以这个点为根,它的每个子树的大小,都不会超过整个树大小的一半(充要条件)
POJ3107
模板
这题卡vector
注意判断数组越界
void dfs(int i,int fa){
siz[i]=1;
int tmp=0;
for(int j=head[i];~j;j=e[j].next){
int v=e[j].to;
if(v!=fa){
dfs(v,i);
siz[i]+=siz[v];
tmp=max(tmp,siz[v]);
}
}
tmp=max(tmp,n-siz[i]);
if(tmp<minn){
minn=tmp;
pos=1;
ans[pos]=i;
}else if(tmp==minn){
ans[++pos]=i;
}
}
CF359(div1)B Kay and Snowflake
给定一棵树,根为\(1\),找到所有子树的重心
性质
设当前子树的根为\(u\),子节点为\(v_i\),\(hc(u)\)表示以\(u\)为根的子树的重心
则\(hc(u)\)在路径\((u,hc(v_i))\)上
即当前子树的重心,在当前子树的子树的重心到当前子树的根的路径上
通过这个性质,这道题首先递归初始化数组,然后自下而上地,让子节点的重心向上跳,来得到父节点的重心
void dfs(int i,int fa){
siz[i]=1;
ans[i]=i;
for(int j=0;j<(int)a[i].size();j++){
int v=a[i][j];
if(v!=fa){
dfs(v,i);
siz[i]+=siz[v];
maxn[i]=max(maxn[i],siz[v]);
}
}
for(int j=0;j<(int)a[i].size();j++){
int v=a[i][j];
if(v!=fa){
int k=ans[v];
while(k!=i){
if(max(maxn[k],siz[i]-siz[k])<=siz[i]/2){
ans[i]=k;
break;
}else k=f[k];
}
}
}
}
CF670(div2)C Link Cut Centroids
给定一棵节点数为\(n\)的树,删一条边然后加上一条边,使得该树的重心唯一(删掉的边和加上的边可以是同一条)
如果这棵树只有一个重心,就任选一边去删,然后再加回来
如果这棵树有两个重心,那么有这样的性质
这两个重心一定相邻,删掉这两点之间的边,获得的两棵子树大小相等
所以只需让这两棵树大小不相等,从其中一棵树删边,再加到另一棵树上即可
P5666
给定一棵树,求单独删除每一条边后分裂出的两棵子树的重心编号之和
换根+倍增
性质
设当前子树的根为\(u\),重子节点为\(v\),\(hc(u)\)表示以\(u\)为根的子树的重心
若\(x\)不是\(hc(u)\),则\(hc(u)\)在以\(v\)为根的子树上
即如果根不是当前子树的重心,那么当前子树的重心在重子子树上
于是定义\(hs[u][k]\)表示\(u\)节点向重子方向跳\(2^k\)步所到达的节点
\(hs[u][0]\)即为\(u\)节点的重子
对于每一条边\((u,v)\),不妨设\(u\)为父节点,\(v\)为子节点
分别处理以\(u\)为根和以\(v\)为根的两棵子树
对于\(v\)子树
直接向下倍增找到重心即可
判断重心利用性质一个点是重心,等价于,以这个点为根,它的每个子树的大小,都不会超过整个树大小的一半(充要条件)
对于\(u\)子树
首先维护\(siz[u]\)
然后考虑到\(v\)节点可能是\(u\)节点的重子,而此时两棵树已经分裂,所以要额外维护各个节点的次重子
定义\(shs[u]\)表示\(u\)节点的次重子
所以当\(v==hs[u][0]\)时,令\(hs[u][0]=shs[u]\)
另外,当前\(u\)节点的重子还有可能是\(u\)节点的父节点
定义\(f[u]\)表示\(u\)节点的父节点
如果\(siz[f[u]]>siz[hs[u][0]]\),令\(hs[u][0]=f[u]\)
由于重子的改变,所以更新\(hs\)数组
最后向下倍增找到重心即可
分别处理完以\(u\)为根和以\(v\)为根的两棵子树后
维护\(f[u]=v\),然后递归
在处理完所有\(u\)节点的子节点\(v\)后,恢复\(hs[u][0],siz[u],f[u]\)和\(hs\)数组
ps:对于存在两个重心的情况,判断倍增得到的重心的父节点是否为重心
void init(int i,int fa){
siz[i]=1;
for(int j=head[i];~j;j=e[j].next){
int v=e[j].v;
if(v==fa)continue;
f[v]=i;
init(v,i);
siz[i]+=siz[v];
if(siz[v]>siz[hs[i][0]]){
shs[i]=hs[i][0];
hs[i][0]=v;
}else if(siz[v]>siz[shs[i]]){
shs[i]=v;
}
}
for(int k=1;k<=20;k++){
hs[i][k]=hs[hs[i][k-1]][k-1];
}
}
bool check(int i,int s){
return max(s-siz[i],siz[hs[i][0]])<=(s/2);
}
void update(int i){
for(int k=1;k<=20;k++){
hs[i][k]=hs[hs[i][k-1]][k-1];
}
}
void dfs(int i,int fa){
int prehs=hs[i][0],presiz=siz[i];
for(int j=head[i];~j;j=e[j].next){
int v=e[j].v;
if(v==fa)continue;
if(v==prehs)hs[i][0]=shs[i];
else hs[i][0]=prehs;
if(siz[fa]>siz[hs[i][0]])hs[i][0]=fa;
update(i);
siz[i]=n-siz[v],f[i]=0,f[v]=0;
int p=i;
for(int k=20;~k;k--)
if(siz[i]-siz[hs[p][k]]<=(siz[i]/2))
p=hs[p][k];
ans+=p+f[p]*check(f[p],siz[i]);
p=v;
for(int k=20;~k;k--)
if(siz[v]-siz[hs[p][k]]<=(siz[v]/2))
p=hs[p][k];
ans+=p+f[p]*check(f[p],siz[v]);
f[i]=v;
dfs(v,i);
}
hs[i][0]=prehs,siz[i]=presiz,f[i]=fa,update(i);
}
标签:子树,重心,int,siz,hs,节点
From: https://www.cnblogs.com/wertyuio1/p/18370344