一、题目
二、解法
这道题换根操作很有意思,容易发现换根对修改是没有影响的,考虑根对询问 u u u的影响:
- u u u就是 r t rt rt,直接输出整棵树的最小值
-
r
t
rt
rt在
u
u
u的子树内,考虑找出
u
u
u在
r
t
rt
rt方向上的第一个儿子
v
v
v,答案就是
全集-v子树
,由于 v v v子树的连续的一段,我们可以把询问拆成两段,分别是 [ 1 , n u m v − 1 ] , [ n u m v + s i z v , n ] [1,num_v-1],[num_v+siz_v,n] [1,numv−1],[numv+sizv,n], n u m num num是树剖的编号, s i z siz siz是子树的大小。 - r t rt rt在 u u u的子树为,没影响,直接询问 [ n u m u , n u m u + s i z u − 1 ] [num_u,num_u+siz_u-1] [numu,numu+sizu−1]的最小值。
#pragma GCC optimize(2)
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#define int long long
#define inf (1ll<<60)
using namespace std;
const int M = 100005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}//区间修改,区间最小值
int n,m,rt,tot,a[M],c[M],siz[M],f[M],fa[M];
int Index,son[M],dep[M],num[M],top[M];
struct edge
{
int v,next;
edge(int V=0,int N=0) : v(V) , next(N) {}
}e[2*M];
struct node
{
int mi,la;
node(int M=0,int L=0) : mi(M) , la(L) {}
node operator + (const node &B) const {
return node(min(mi,B.mi),0);
}
}tr[4*M];
void dfs1(int u,int p)
{
fa[u]=p;
dep[u]=dep[p]+1;
siz[u]=1;
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==p) continue;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])
son[u]=v;
}
}
void dfs2(int u,int tp)
{
top[u]=tp;
num[u]=++Index;
c[Index]=a[u];
if(son[u]) dfs2(son[u],tp);
for(int i=f[u];i;i=e[i].next)
if(e[i].v^son[u] && e[i].v^fa[u])
dfs2(e[i].v,e[i].v);
}
void build(int i,int l,int r)
{
if(l==r)
{
tr[i]=node(c[l],0);
return ;
}
int mid=(l+r)>>1;
build(i<<1,l,mid);
build(i<<1|1,mid+1,r);
tr[i]=tr[i<<1]+tr[i<<1|1];
}
void down(int i)
{
if(tr[i].la==0) return ;
tr[i<<1]=tr[i<<1|1]=node(tr[i].la,tr[i].la);
tr[i].la=0;
}
void updata(int i,int l,int r,int L,int R,int v)
{
if(l>R || L>r) return ;
if(L<=l && r<=R)
{
tr[i]=node(v,v);
return ;
}
down(i);
int mid=(l+r)>>1;
updata(i<<1,l,mid,L,R,v);
updata(i<<1|1,mid+1,r,L,R,v);
tr[i]=tr[i<<1]+tr[i<<1|1];
}
int query(int i,int l,int r,int L,int R)
{
if(l>R || L>r || L>R) return inf;
if(L<=l && r<=R) return tr[i].mi;
down(i);
int mid=(l+r)>>1;
return min(query(i<<1,l,mid,L,R),query(i<<1|1,mid+1,r,L,R));
}
void modify(int u,int v,int c)
{
while(top[u]^top[v])
{
if(dep[top[u]]<=dep[top[v]]) swap(u,v);
updata(1,1,n,num[top[u]],num[u],c);
u=fa[top[u]];
}
if(dep[u]<=dep[v]) swap(u,v);
updata(1,1,n,num[v],num[u],c);
}
int Find(int x,int y)//y在x方向的第一个儿子
{
while(top[x]^top[y])
{
if(dep[top[x]]<=dep[top[y]]) swap(x,y);
if(fa[top[x]]==y) return top[x];
x=fa[top[x]];
}
return son[y];
}
int Lca(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<=dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
return dep[x]>=dep[y]?y:x;
}
signed main()
{
n=read();m=read();
for(int i=1;i<n;i++)
{
int u=read(),v=read();
e[++tot]=edge(v,f[u]),f[u]=tot;
e[++tot]=edge(u,f[v]),f[v]=tot;
}
for(int i=1;i<=n;i++)
a[i]=read();
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
rt=read();
while(m--)
{
int op=read();
if(op==1)
rt=read();
if(op==2)
{
int u=read(),v=read(),c=read();
modify(u,v,c);
}
if(op==3)
{
int u=read();
if(u^rt && u==Lca(u,rt))//子树内
{
int t=Find(rt,u);
printf("%lld\n",min(query(1,1,n,1,num[t]-1),query(1,1,n,siz[t]+num[t],n)));
}
else if(u==rt)
printf("%lld\n",query(1,1,n,1,n));
else
printf("%lld\n",query(1,1,n,num[u],num[u]+siz[u]-1));
}
}
}