题意:
给你一棵树,要支持下列操作:1.链赋值2.子树min3.换根,要nlogn
题解:
这题看了几个题解完全没有说换根是怎么维护的,都在说看代码,一气之下就自己想出了这个题。
链赋值和子树min很容易想到树剖,但是树剖不支持换根。我太弱不会top tree,只能想办法解决换根问题。
我们考虑换根对答案的影响,我们画个图应该不难看出换根会影响的只有新的根到原来根的路径上的点。那么询问其他点的话答案不变。我们考虑新根到原来根的路径上的点,答案的变化是去掉新根所在的子树,加上原来父节点变成新的子树。我们不能重新dfs,所以我们要想办法用原来的dfs序求出新的答案。我们可以分类讨论来求出答案。如果询问的点是当前根,我们可以直接用整棵树的min。我们设询问的点是x,那么如果x与当前根的lca不是x本身,那么就意味着当前的根不在x的子树中,那么要不然x在当前根的子树,要么x和当前根不在初始根的同一个子树中。对于这种情况,答案和初始情况没有发生变化。要是x与当前根的lca是x本身,那么意味着x在原始根和现在的根之间,那么当前询问的答案应该是在原始树中除去当前根所在的x的子树之后其他所有点的min,于是我们需要通过倍增和树剖时算出的深度求出根rt向上 d e p [ r t ] − d e p [ x ] − 1 dep[rt]-dep[x]-1 dep[rt]−dep[x]−1次之后的祖先,也就是x对应的向当前根方向走一步的点,设为z。我们其实相当于用所有的点去掉以原树中以z为根的子树,在dfs序中就是 [ 1 , z 的 d f s 序 的 前 一 个 ] [1,z的dfs序的前一个] [1,z的dfs序的前一个]和 [ z 所 在 子 树 的 d f s 序 的 最 后 一 个 数 的 再 下 一 个 , n ] [z所在子树的dfs序的最后一个数的再下一个,n] [z所在子树的dfs序的最后一个数的再下一个,n]。这样我们就可以用原始的dfs序来维护换根了。
代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,hed[100010],cnt,rt,b[100010],fa[100010],dep[100010],sz[100010],son[100010];
int ed[100010],ys[100010],yss[100010],z,tp[100010];
int f[100010][20];
struct node
{
int to,next;
}a[200010];
struct tree
{
int l,r,mn,tag;
}tr[400010];
inline void add(int from,int to)
{
a[++cnt].to=to;
a[cnt].next=hed[from];
hed[from]=cnt;
}
inline void dfs(int x)
{
sz[x]=1;
for(int i=1;i<=19;++i)
f[x][i]=f[f[x][i-1]][i-1];
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
if(y!=fa[x])
{
dep[y]=dep[x]+1;
fa[y]=x;
f[y][0]=x;
dfs(y);
sz[x]+=sz[y];
if(sz[y]>sz[son[x]])
son[x]=y;
}
}
}
inline void dfs2(int x,int top)
{
ys[x]=++z;
yss[z]=x;
tp[x]=top;
if(son[x])
{
dfs2(son[x],top);
for(int i=hed[x];i;i=a[i].next)
{
int y=a[i].to;
if(y!=fa[x]&&y!=son[x])
dfs2(y,y);
}
}
ed[x]=z;
}
inline void pushup(int rt)
{
tr[rt].mn=min(tr[rt<<1].mn,tr[rt<<1|1].mn);
}
inline void build(int rt,int l,int r)
{
tr[rt].l=l;
tr[rt].r=r;
if(l==r)
{
tr[rt].mn=b[yss[l]];
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
pushup(rt);
}
inline void pushdown(int rt)
{
if(tr[rt].tag)
{
tr[rt<<1].mn=tr[rt].tag;
tr[rt<<1|1].mn=tr[rt].tag;
tr[rt<<1].tag=tr[rt].tag;
tr[rt<<1|1].tag=tr[rt].tag;
tr[rt].tag=0;
}
}
inline void update(int rt,int le,int ri,int z)
{
int l=tr[rt].l,r=tr[rt].r;
if(le<=l&&r<=ri)
{
tr[rt].mn=z;
tr[rt].tag=z;
return;
}
pushdown(rt);
int mid=(l+r)>>1;
if(le<=mid)
update(rt<<1,le,ri,z);
if(mid+1<=ri)
update(rt<<1|1,le,ri,z);
pushup(rt);
}
inline void change(int x,int y,int z)
{
int fx=tp[x],fy=tp[y];
while(fx!=fy)
{
if(dep[fx]<dep[fy])
{
swap(x,y);
swap(fx,fy);
}
update(1,ys[fx],ys[x],z);
x=fa[fx];
fx=tp[x];
}
if(dep[x]<dep[y])
update(1,ys[x],ys[y],z);
else
update(1,ys[y],ys[x],z);
}
inline int query(int rt,int le,int ri)
{
int l=tr[rt].l,r=tr[rt].r;
if(l>ri||r<le)
return 0;
if(le<=l&&r<=ri)
return tr[rt].mn;
pushdown(rt);
int mid=(l+r)>>1,ans=2147483647;
if(le<=mid)
ans=min(ans,query(rt<<1,le,ri));
if(mid+1<=ri)
ans=min(ans,query(rt<<1|1,le,ri));
return ans;
}
inline int lca(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
for(int i=19;i>=0;--i)
{
if(dep[f[x][i]]>=dep[y])
x=f[x][i];
}
if(x==y)
return x;
for(int i=19;i>=0;--i)
{
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
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",&b[i]);
scanf("%d",&rt);
dep[rt]=1;
dfs(rt);
dfs2(rt,rt);
build(1,1,n);
for(int i=1;i<=m;++i)
{
int opt;
scanf("%d",&opt);
if(opt==1)
{
int x;
scanf("%d",&x);
rt=x;
}
if(opt==2)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
change(x,y,z);
}
if(opt==3)
{
int x;
scanf("%d",&x);
if(x==rt)
printf("%d\n",query(1,1,n));
else
{
int z=lca(x,rt);
if(z!=x)
printf("%d\n",query(1,ys[x],ed[x]));
else
{
int qaq=dep[rt]-dep[x]-1;
z=rt;
for(int i=19;i>=0;--i)
{
if((1<<i)<=qaq)
{
z=f[z][i];
qaq-=(1<<i);
}
}
int ans=2147483647;
if(ys[z]>1)
ans=query(1,1,ys[z]-1);
if(ed[z]+1<=n)
ans=min(ans,query(1,ed[z]+1,n));
printf("%d\n",ans);
}
}
}
}
return 0;
}