一开始看到有换根操作可是把我吓了一跳,我还以为要上top-tree呢,结果一开题解。。。。。。。。。。心塞
如果除去换根操作的话就是一道裸的树链剖分了,没什么难度,但是加上换根操作以后,别怕,其实联系查询的点和现在的根节点只有4种可能(分3种操作),这里的讨论均基于以1为根节点的有根树讨论
1.x==rt 直接查询整个树
2.rt是x的祖先 直接查询x为根的子树即可
3.x是rt的祖先 令u为x到rt这一段路径离rt最近的节点,答案就是整个树除了u这个子树的最小点权
4.x和rt不是祖孙关系 直接查询x为根的子树即可
然后返现2,4可以一起讨论,求一下lca就好判断了。。。。ok
#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 100021
#define ls u<<1,l,mid
#define rs u<<1|1,mid+1,r
using namespace std;
int nu[maxn],F[maxn],p[maxn][20],size[maxn],n,m,son[maxn],head[maxn],rt;
int h[maxn],tot,etot=1,f[maxn],top[maxn],in[maxn],out[maxn],cnt,val[maxn];
struct edge{int v,next;}e[maxn*2];
void adde(int a,int b){e[etot].v=b,e[etot].next=head[a];head[a]=etot++;}
/****************剖分啊*********************/
void dfs1(int u,int fa){
size[u]=1,f[u]=fa,h[u]=h[fa]+1,p[u][0]=fa;
for(int i=1;i<=17;i++)p[u][i]=p[p[u][i-1]][i-1];
for(int v,i=head[u];i;i=e[i].next){
if((v=e[i].v)==fa)continue;
dfs1(v,u);
size[u]+=size[v];
if(size[v]>size[son[u]])son[u]=v;
}
}
void dfs2(int u,int fa,int tt){
nu[u]=++tot,F[tot]=u,in[u]=++cnt,top[u]=tt;
if(son[u])dfs2(son[u],u,tt);
for(int v,i=head[u];i;i=e[i].next){
if((v=e[i].v)==fa||v==son[u])continue;
dfs2(v,u,v);
}
out[u]=cnt;
}
/****************线段树************************/
int Min[maxn*4],lz[maxn*4];
inline void push_down(int u){
if(!lz[u])return;
Min[u<<1]=lz[u],Min[u<<1|1]=lz[u];
lz[u<<1]=lz[u],lz[u<<1|1]=lz[u];
lz[u]=0;
}
inline void push_up(int u){Min[u]=min(Min[u<<1],Min[u<<1|1]);}
void build(int u,int l,int r){
if(l==r){Min[u]=val[F[l]];return;}
int mid=l+r>>1;
build(ls),build(rs);
push_up(u);
}
void update(int u,int l,int r,int x,int y,int add){
if(x==l&&r==y){
Min[u]=lz[u]=add;
return;
}int mid=l+r>>1;
push_down(u);
if(x>mid)update(rs,x,y,add);
else if(y<=mid)update(ls,x,y,add);
else update(ls,x,mid,add),update(rs,mid+1,y,add);
push_up(u);
}
int query(int u,int l,int r,int x,int y){
if(x==l&&r==y)return Min[u];
int mid=l+r>>1;
push_down(u);
if(x>mid)return query(rs,x,y);
else if(y<=mid)return query(ls,x,y);
else return min(query(ls,x,mid),query(rs,mid+1,y));
}
/****************************剖分操作******************************/
void change(int a,int b,int c){
while(top[a]!=top[b]){
if(h[top[a]]>h[top[b]])swap(a,b);
update(1,1,n,nu[top[b]],nu[b],c);
b=f[top[b]];
}
if(h[a]>h[b])swap(a,b);
update(1,1,n,nu[a],nu[b],c);
}
int lca(int a,int b){
while(top[a]!=top[b]){
if(h[top[a]]>h[top[b]])swap(a,b);
b=f[top[b]];
}return h[a]>h[b] ? b : a;
}
int main(){
scanf("%d%d",&n,&m);
for(int a,b,i=1;i<n;i++){
scanf("%d%d",&a,&b);
adde(a,b),adde(b,a);
}
for(int i=1;i<=n;i++)scanf("%d",val+i);
dfs1(1,1);dfs2(1,1,1);
build(1,1,n);
scanf("%d",&rt);int pos,a,b,c;
while(m--){
scanf("%d",&pos);
if(pos==1){
scanf("%d",&a);rt=a;
}else if(pos==2){
scanf("%d%d%d",&a,&b,&c);
change(a,b,c);
}else{
scanf("%d",&a);
if(a==rt){
printf("%d\n",query(1,1,n,1,n));
}else if(lca(rt,a)!=a){
printf("%d\n",query(1,1,n,in[a],out[a]));
}else{
int b=rt;
for(int i=17;i>=0;i--)if(h[p[b][i]]>h[a])b=p[b][i];
int ans=query(1,1,n,1,in[b]-1);
if(out[b]<n)ans=min(ans,query(1,1,n,out[b]+1,n));
printf("%d\n",ans);
}
}
}
return 0;
}