首页 > 技术文章 > 关于树链剖分 "换根操作" 笔记

FarkasW 2020-11-04 19:13 原文

\[\text{前言} \]

\(\quad\)最近在你谷上做了几道"换根操作"的树链剖分题,觉得有必要记录一下,以免以后忘记了。(如果没有见过这种题的可以去做做P3979 遥远的国度CF916E Jamie and Tree)

\[\text{关于题目要求的操作} \]

\(\quad\)相信你已经看过这两道题了(没看过也没有没关系),其实可以发现在一棵树中,只有父亲(祖先),子树,深度,会因为根节点的变化而变化,所以题目一般需要你有换根操作,子树修改操作,求 \(LCA\) (最近公共祖先),我们分别来考虑一下。(可以看看下面这张图来理解,题目中的图)

)

\[\text{换根} \]

\(\quad\)因为每换一次根,树中的很多信息都会改变,不可能每次换根都跑两便 \(dfs\) 预处理,所以我们考虑其他方法,对于单纯的换根操作,只需要设置一个全局变量 \(root\) 来存储根的编号( \(root\) 初始化为 \(1\) ,默认以 \(1\) 为根),对于其他操作,再通过分类讨论 \(root\) 的位置来进行操作。

\[\text{LCA(最近公共祖先)} \]

\(\quad\)因为这题我们肯定用树链剖分解题,所以对于原图( \(root==1\) )的情况下 \(LCA\) 的求法肯定是使用树链剖分的(当然如果读者愿意专门打个倍增,那么你们随意)

\(\quad\)注意:(小写) \(lca(x,y)\) 表示在以1为根的树中 \(x\)\(y\) 的最近公共祖先,(大写) \(LCA(x,y)\) 表示在以 \(root\) 为根的树中 \(x\)\(y\) 的最近公共祖先。

il int lca(int x,int y) //模板树链剖分求LCA
{
  int fx=top[x],fy=top[y];
  while(fx!=fy)
    {
      if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
      x=father[fx];fx=top[x];
    }
  if(dep[x]>dep[y])swap(x,y);
  return x;
}

\(\quad\)接下来我们就要对 \(root\) 的位置进行分类讨论了,代码先贴出来给你们看看。

il int LCA(int x,int y)
{
  if(dep[x]>dep[y])swap(x,y);
  int xr=lca(x,root),yr=lca(y,root),xy=lca(x,y);
  if(xy==x)
  {
  	if(xr==x){if(yr==y)return y;return yr;}
  	return x;
  }
  if(xr==x)return x;if(yr==y)return y;
  if((xr==root&&xy==yr)||(yr==root&&xy==xr))return root;
  if(xr==yr)return xy;
  if(xy!=xr)return xr;return yr;
}

\(\quad\)另外我们可以再画几张图来方便理解。

一.当 \(lca(x,y)==x\) (可以先按深度调序, \(dep[x]<=dep[y]\))


\(\quad\) \(1\). 情况 \(1\)\(root\)\(x\) 的子树中,也在 \(y\) 的子树中,即 \(lca(x,root)==x\) && \(lca(y,root)==y\) ,此时 \(LCA(x,y)\)\(y\) ,因为图要反过来看(以 \(root\) 为根)

\(\quad\) \(2\). 情况 \(2\)\(root\)\(x\) 的子树中,但不在 \(y\) 的子树中,即 \(lca(x,root)\) ,此时 \(LCA(x,y)\)\(lca(y,root)\)

\(\quad\) \(3\). 情况 \(3\) :其他情况下, \(LCA(x,y)\) 就是 \(x\)

二.当 \(lca(x,y)!=x\) (因为 \(dep[x]<=dep[y]\),所以 \(lca(x,y)!=y\)\(x\) , \(y\) 在不同子树上)

\(\quad\) 1. 情况1:( \(lca(x,root)==x\) )||( \(lca(x,root)==x\) ),root在x(或y)的子树中时, \(LCA(x,y)\)\(x\) (或 \(y\) ),显然。

\(\quad\) 2. 情况2:( \(lca(x,root)==root\) && \(lca(x,y)==lca(y,root)\) )||( \(lca(y,root)==root\) && \(lca(x,y)==lca(x,root)\)),即 \(root\)\(x\)\(y\) 的简单路径上时,答案为 \(root\) 。(也可以用深度判断, ( \(lca(x,root)===root\) && \(dep[root]>=dep[lca(x,y)]\) )||( \(lca(y,root)==root\) && \(dep[root]>=dep[lca(x,y)]\) ))

\(\quad\) 3. 情况3: \(lca(x,root)==lca(y,root)\) ,即 \(root\) 在上方时,\(LCA(x,y)\)\(lca(x,y)\)

\(\quad\) 4. 情况4:当 \(root\)\(x\)\(y\) 的链上节点的子树中时, \(LCA(x,y)\) 为那个链上节点。

\(\quad\)这样就把树上所有 \(root\) 位置的情况都考虑到了,不重不漏。

\[\text{子树修改(查询)} \]

\(\quad\) 情况 \(1\) :当 \(x=root\) 时, \(x\) 就是此时整棵树的根,那么就是全局修改(查询)。

\(\quad\) 情况 \(2\) :当 \(root\) 在x子树中时,就需要特别判断了,根据图像我们可以发现此时x的真正子树是包括除了 \(root\) 方向上的子树之外其他所有节点。

\(\quad\) 情况 \(3\) :其他情况下 \(x\) 的子树以 \(root\) 为根和以 \(1\) 为根是一样的。

\(\quad\) 以上就是关于树链剖分 "换根操作" 笔记的全部内容了,如果喜欢,不妨点个赞吧!

推荐阅读