有些树上操作题目需要换根操作,用普通的树链剖分无法解决此类问题。这时我们就需要改造一下树剖,令它支持换根操作。
首先肯定不可能每次都重新剖分,因为那样会 TLE。那么考虑以 为根剖,记下当前的跟 ,对于每个操作特殊处理。
- 最近公共祖先
考虑用 表示以 为根时 和 的最近公共祖先,用 表示换根后 和 的最近公共祖先(这里默认 深度小于等于 )。
- 若 :(图中绿色节点是 ,红色节点是 ,不同颜色的节点是不同情况下 的位置)

(1) 若 位于黄色区域,即 ,那么
(2) 若 位于橙色区域,即 ,那么
(3) 若 位于粉色区域,即 ,那么
- 若 :(图中绿色节点是 ,红色节点是 ,不同颜色的节点是不同情况下 的位置)

(1) 若 位于灰色区域,即 ,那么
(2) 若 位于蓝色区域,即 ,那么
(3) 若 位于粉色区域,即 ,那么
(4) 若 位于橙色区域,即 ,那么
(5) 若以上条件均不满足,那么若 ,那么 ,否则
代码如下:
inline int getLCA(int x,int y)
{
if(dep[x]>dep[y])
{
swap(x,y);
}
int xr=getlca(x,rt),yr=getlca(y,rt),xy=getlca(x,y);
if(xy==x)
{
if(xr==x&&yr==y)
{
return y;
}
if(xr==x)
{
return yr;
}
return x;
}
else
{
if(xr==x||yr==y)
{
return xr==x?x:y;
}
if(xr==yr)
{
return xy;
}
if((xr==rt&&yr==xy)||(xr==xy&&yr==rt))
{
return rt;
}
return xr==xy?yr:xr;
}
}
- 链上询问/修改
显然无论根怎么换,链 上的节点都不会改变,所以直接修改即可。
- 子树内询问/修改
图中绿色节点是 。

-
若 ,那么查询/修改整棵子树
-
若 位于黄色区域,即 $\operatorname{lca}(x,rt)=x,那么查询/修改 子树除了 方向儿子的子树外所有的节点
-
否则说明 位于粉色区域,那么查询/求改 子树即可
代码如下:
inline int jump(int x,int k)
{
for(int i=25;i>=0;i--)
{
if(k&(1<<i))
{
x=up[x][i];
}
}
return x;
}
inline int quesubtree(int x)
{
if(x==rt)
{
return que(1,1,n,1,n);
}
int xr;
if(dep[rt]>dep[x]&&up[xr=jump(rt,dep[rt]-dep[x]-1)][0]==x)
{
if(id[xr]==1)
{
return que(1,1,n,R[xr]+1,n);
}
if(R[xr]==n)
{
return que(1,1,n,1,id[xr]-1);
}
return min(que(1,1,n,1,id[xr]-1),que(1,1,n,R[xr]+1,n));
}
return que(1,1,n,id[x],R[x]);
}
模板题代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
const long long S=1000005,MS=100005;
const int inf=2147483647;
int n,m,val[MS];
int esum,to[S],nxt[S],h[MS];
int up[MS][30],dep[MS],siz[MS],hson[MS];
int cnt,id[MS],top[MS],a[MS],R[MS];
int minn[MS<<2],lazy[MS<<2];
int rt;
inline void add(int x,int y)
{
to[++esum]=y;
nxt[esum]=h[x];
h[x]=esum;
}
void dfs1(int u,int fa)
{
up[u][0]=fa;
for(int i=1;i<=25;i++)
{
up[u][i]=up[up[u][i-1]][i-1];
}
dep[u]=dep[fa]+1;
siz[u]=1;
for(int i=h[u];i;i=nxt[i])
{
int v=to[i];
if(v==fa)
{
continue;
}
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[hson[u]])
{
hson[u]=v;
}
}
}
void dfs2(int u,int tpf)
{
id[u]=++cnt;
top[u]=tpf;
a[cnt]=val[u];
if(hson[u]!=0)
{
dfs2(hson[u],tpf);
}
for(int i=h[u];i;i=nxt[i])
{
int v=to[i];
if(v==up[u][0]||v==hson[u])
{
continue;
}
dfs2(v,v);
}
R[u]=cnt;
}
void build(int u,int l,int r)
{
lazy[u]=-1;
if(l==r)
{
minn[u]=a[l];
return;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
minn[u]=min(minn[u<<1],minn[u<<1|1]);
}
void upd(int u,int l,int r,int L,int R,int k)
{
if(l>R||r<L)
{
return;
}
if(l>=L&&r<=R)
{
minn[u]=k;
lazy[u]=k;
return;
}
if(lazy[u]!=-1)
{
minn[u<<1]=lazy[u];
minn[u<<1|1]=lazy[u];
lazy[u<<1]=lazy[u];
lazy[u<<1|1]=lazy[u];
lazy[u]=-1;
}
int mid=l+r>>1;
if(L<=mid)
{
upd(u<<1,l,mid,L,R,k);
}
if(R>=mid+1)
{
upd(u<<1|1,mid+1,r,L,R,k);
}
minn[u]=min(minn[u<<1],minn[u<<1|1]);
}
int que(int u,int l,int r,int L,int R)
{
if(l>R||r<l)
{
return inf;
}
if(l>=L&&r<=R)
{
return minn[u];
}
if(lazy[u]!=-1)
{
minn[u<<1]=lazy[u];
minn[u<<1|1]=lazy[u];
lazy[u<<1]=lazy[u];
lazy[u<<1|1]=lazy[u];
lazy[u]=-1;
}
int mid=l+r>>1,res=inf;
if(L<=mid)
{
res=min(res,que(u<<1,l,mid,L,R));
}
if(R>=mid+1)
{
res=min(res,que(u<<1|1,mid+1,r,L,R));
}
return res;
}
inline int jump(int x,int k)
{
for(int i=25;i>=0;i--)
{
if(k&(1<<i))
{
x=up[x][i];
}
}
return x;
}
inline int getlca(int x,int y)
{
if(dep[x]<dep[y])
{
swap(x,y);
}
for(int i=25;i>=0;i--)
{
if(dep[up[x][i]]>=dep[y])
{
x=up[x][i];
}
}
if(x==y)
{
return x;
}
for(int i=25;i>=0;i--)
{
if(up[x][i]!=up[y][i])
{
x=up[x][i];
y=up[y][i];
}
}
return up[x][0];
}
inline int getLCA(int x,int y)
{
if(dep[x]>dep[y])
{
swap(x,y);
}
int xr=getlca(x,rt),yr=getlca(y,rt),xy=getlca(x,y);
if(xy==x)
{
if(xr==x&&yr==y)
{
return y;
}
if(xr==x)
{
return yr;
}
return x;
}
else
{
if(xr==x||yr==y)
{
return xr==x?x:y;
}
if(xr==yr)
{
return xy;
}
if((xr==rt&&yr==xy)||(xr==xy&&yr==rt))
{
return rt;
}
return xr==xy?yr:xr;
}
}
inline void updpath(int x,int y,int k)
{
while(top[x]!=top[y])
{
if(dep[top[x]]>dep[top[y]])
{
upd(1,1,n,id[top[x]],id[x],k);
x=up[top[x]][0];
}
else
{
upd(1,1,n,id[top[y]],id[y],k);
y=up[top[y]][0];
}
}
upd(1,1,n,min(id[x],id[y]),max(id[x],id[y]),k);
}
inline int quesubtree(int x)
{
if(x==rt)
{
return que(1,1,n,1,n);
}
int xr;
if(dep[rt]>dep[x]&&up[xr=jump(rt,dep[rt]-dep[x]-1)][0]==x)
{
if(id[xr]==1)
{
return que(1,1,n,R[xr]+1,n);
}
if(R[xr]==n)
{
return que(1,1,n,1,id[xr]-1);
}
return min(que(1,1,n,1,id[xr]-1),que(1,1,n,R[xr]+1,n));
}
return que(1,1,n,id[x],R[x]);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n-1;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&val[i]);
}
scanf("%d",&rt);
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
while(m--)
{
int op;
scanf("%d",&op);
if(op==1)
{
scanf("%d",&rt);
}
else if(op==2)
{
int x,y,k;
scanf("%d%d%d",&x,&y,&k);
updpath(x,y,k);
}
else
{
int x;
scanf("%d",&x);
printf("%d\n",quesubtree(x));
}
}
return 0;
}