【树】树上启发式合并
树上启发式合并
学习资料:OI Wiki 树上启发式合并
时间复杂度:\(\mathcal{O}(nlogn)\)
作用:
树上启发式合并 在计算的时候,保留重儿子的子树答案,清除轻儿子子树的带来更新,再将当前树的所有轻儿子影响的更新重新计算合并到重儿子的更新上。用于计算所有子树的答案。
模板:
vector<int>p[maxn];
int siz[maxn],son[maxn],fa[maxn];
void dfs1(int u,int f)
{
siz[u]=1;son[u]=0;fa[u]=f;
for(int i=0;i<p[u].size();i++)
{
if(p[u][i]==f)continue;
dfs1(p[u][i],u);
siz[u]+=siz[p[u][i]];
if(siz[p[u][i]]>siz[son[u]])son[u]=p[u][i];
}
}
void merge(int u,int v)//v是最外层树的那个重儿子
{
/*
记录u节点对答案的更新
*/
for(int i=0;i<p[u].size();i++)
if(p[u][i]!=fa[u]&&p[u][i]!=v)
merge(p[u][i],v);
}
void clear(int u)
{
/*
撤销u节点的更新
*/
for(int i=0;i<p[u].size();i++)
if(p[u][i]!=fa[u])clear(p[u][i]);
}
void cal(u)
{
;
}
void dfs2(int u,bool is)//is=1表示当前子树是重儿子
{
for(int i=0;i<p[u].size();i++)
if(p[u][i]!=fa[u]&&p[u][i]!=son[u])
dfs2(p[u][i],0);
if(son[u])dfs2(son[u],1);
merge(u,son[u]);//合并
cal(u);//计算
if(!is)clear(u);//轻儿子则清除更新
}
关于时间复杂度:
根节点到树上任意节点的轻便数不超过 \(logn\) 条 ,对于每个轻儿子,会遍历的它子树包含的所有的点,所以总的时间复杂度是 \(\mathcal{O(nlogn)}\)。