【介绍】
由于树具有一般的图没有的特点,所以在竞赛中的应用更广。
在一些树上路径问题中,暴力求解时间复杂度过高,往往需要一些更为高效的算法,点分治就是其中之一。
【流程】
首先选取一个点,把无根树变成有根树。
我们可以用树型DP选点。
因为树是递归定义的,所以我们当然希望递归的层数最小。
每次选取的“重心”,(就是相连的结点数最多的连通块的结点数最小的点)
void getrot(int x,int fa)
{
f_son[x]=1; f[x]=0;//f数组表示当x为根是,以x的子节点为根的最大子树的大小,f_son数组表示以x为根的树的大小
for (int j=lnk[x]; j; j=nxt[j])
if (!vis[son[j]]&&son[j]!=fa)
{
getrot(son[j],x);
f_son[x]+=f_son[son[j]];
f[x]=max(f[x],f_son[son[j]]);
}
f[x]=max(f[x],nn-f_son[x]);//考虑以x的父节点为根的子树的大小。
if (f[x]<f[rot]) rot=x;//修正当前的根rot。
}
【核心代码】
void solve(int x)
{
vis[x]=1;
for (int j=lnk[x]; j; j=nxt[j])
if (!vis[son[j]])
{
nn=f_son[son[j]]; rot=0;
getrot(son[j],0);//接着遍历子树。
solve(rot);
}
}
int main()
{
memset(vis,0,sizeof(vis));
memset(lnk,0,sizeof(lnk));
tot=rot=0; nn=n; f[0]=INF;
getrot(1,0);//找根
solve(rot);//点分
printf("%d\n",ans);
n=read_(); m=read_();
}