可以发现,如果每次换根操作都重新跑一遍树剖是会T到死的。
因为树的形态不变,考虑分类讨论一下换根操作对答案的影响。
如果当前根等于\(X\)则直接输出整个树的最小值。
如果\(LCA(X,root)≠X\),对答案没有任何影响,直接查询。
如果\(LCA(X,root)=x\),发现除了以从\(X\)到\(root\)的最近的那个点为根的子树,其他都是\(X\)的子孙,因为一颗子树内的\(dfn\)都是连续的,直接在查询的时候把这个区间筛掉即可。
#include <bits/stdc++.h>
#define lson (o<<1)
#define rson (o<<1|1)
#define mid (l+r>>1)
typedef long long ll;
const int max_n=100000+5;
const int inf=2147483647;
int N,M,root,cnt,tot;
int size[max_n],top[max_n],son[max_n],father[max_n],depth[max_n],dfn[max_n],first_edge[max_n];
ll wt[max_n],W[max_n];
struct Segment_tree
{
ll minv,lazy;
Segment_tree()
{
minv=lazy=0;
}
}tree[max_n<<2];
struct Edge
{
int to,next_edge;
}edge[max_n<<1];
inline int read()
{
register ll x=0;
register char ch=getchar();
while(!isdigit(ch))
ch=getchar();
while(isdigit(ch))
{
x=(x<<1)+(x<<3)+ch-‘0‘;
ch=getchar();
}
return x;
}
inline void add_edge(int x,int y)
{
edge[++tot].to=y;
edge[tot].next_edge=first_edge[x];
first_edge[x]=tot;
}
inline void push_up(int o)
{
tree[o].minv=std::min(tree[lson].minv,tree[rson].minv);
}
inline void push_down(int o)
{
if(tree[o].lazy)
{
tree[lson].lazy=tree[rson].lazy=tree[o].lazy;
tree[lson].minv=tree[rson].minv=tree[o].lazy;
tree[o].lazy=0;
}
}
void build(int o,int l,int r)
{
if(l==r)
{
tree[o].minv=wt[l];
return;
}
build(lson,l,mid),build(rson,mid+1,r);
push_up(o);
}
void update(int o,int l,int r,int ql,int qr,int v)
{
if(ql<=l && r<=qr)
{
tree[o].minv=tree[o].lazy=v;
return;
}
push_down(o);
if(ql<=mid) update(lson,l,mid,ql,qr,v);
if(mid<qr) update(rson,mid+1,r,ql,qr,v);
push_up(o);
}
int query(int o,int l,int r,int ql,int qr)
{
if(ql<=l && r<=qr) return tree[o].minv;
int res=inf;
push_down(o);
if(ql<=mid) res=query(lson,l,mid,ql,qr);
if(mid<qr) res=std::min(res,query(rson,mid+1,r,ql,qr));
return res;
}
void dfs1(int x,int fa)
{
depth[x]=depth[fa]+1;
father[x]=fa;
size[x]=1;
int maxx=-1;
for(int k=first_edge[x];k;k=edge[k].next_edge)
{
if(edge[k].to!=fa)
{
dfs1(edge[k].to,x);
size[x]+=size[edge[k].to];
if(size[edge[k].to]>maxx)
{
maxx=size[edge[k].to];
son[x]=edge[k].to;
}
}
}
}
void dfs2(int x,int topf)
{
dfn[x]=++cnt;
wt[cnt]=W[x];
top[x]=topf;
if(!son[x]) return;
dfs2(son[x],topf);
for(int k=first_edge[x];k;k=edge[k].next_edge)
if(edge[k].to!=father[x] && edge[k].to!=son[x]) dfs2(edge[k].to,edge[k].to);
}
int LCA(int x,int y)
{
while(top[x]^top[y])
{
if(depth[top[x]]<depth[top[y]]) std::swap(x,y);
x=father[top[x]];
}
return (depth[x]<depth[y])?x:y;
}
void modify(int x,int y,int z)
{
while(top[x]^top[y])
{
if(depth[top[x]]<depth[top[y]]) std::swap(x,y);
update(1,1,N,dfn[top[x]],dfn[x],z);
x=father[top[x]];
}
if(depth[x]>depth[y]) std::swap(x,y);
update(1,1,N,dfn[x],dfn[y],z);
}
ll calc(int x)
{
if(x==root) return query(1,1,N,1,size[1]);
else if(LCA(x,root)!=x) return query(1,1,N,dfn[x],dfn[x]+size[x]-1);
else
{
int k=root;
while(father[k]!=x)
k=father[k];
return std::min(query(1,1,N,1,dfn[k]-1),query(1,1,N,dfn[k]+size[k],N));
}
}
int main()
{
int opt,x,y,z;
N=read(),M=read();
for(int i=1;i<N;++i)
{
x=read(),y=read();
add_edge(x,y),add_edge(y,x);
}
for(int i=1;i<=N;++i)
W[i]=read();
root=read();
dfs1(root,0),dfs2(root,root);
build(1,1,N);
for(int i=1;i<=M;++i)
{
opt=read();
if(opt==1) root=read();
else if(opt==2)
{
x=read(),y=read(),z=read();
modify(x,y,z);
}
else printf("%lld\n",calc(read()));
}
return 0;
}