传送门
写在前面:链剖裸题里的战斗机
思路:Drusher向我推荐了这道污题,据说他请Claris调了一周才A,于是我怀着忐忑的心情开始看题,其他操作都好,一个换根把我搞懵逼了,“套splay吗?”显然编程复杂度和时间复杂度都是极大的,每次换根又要重新剖分。后来看到了Lcomyn学长的讲解才大体明白,这里大家可以直接点开链接去看一下,但是我在调试第三种情况时出了一些问题,因为我用的是dfs序RMQ求lca,所以倍增求点时会出现一些奇怪的错误,20组数据WA了4组,无法只能借鉴zyf的暴力枚举法,通过记录下的搜索顺序判断x的儿子是否为root的祖先,若是则一定是我们要求的v
注意:小心数组越界,还有就是尽量不要用RMQ算法……
#include<bits/stdc++.h>
#define Inf 0x7fffffff
using namespace std;
int n,m,opt,cnt,tot,root,x,y,z;
int pos[100010],num[400010],pre[100010],top[100010],fa[100010],son[100010],L[100010],R[100010],dep[100010],siz[100010],def[100010],first[100010];
int f[400010][20];
struct os
{
int u,v,next;
}e[200010];
struct node
{
int minn,lazy;
}tree[400010];
void add(int x,int y)
{
e[++tot].u=x;
e[tot].v=y;
e[tot].next=first[x];
first[x]=tot;
}
void pushdown(int now)
{
if (!tree[now].lazy) return;
tree[now<<1].lazy=tree[now<<1].minn=tree[now].lazy;
tree[now<<1|1].lazy=tree[now<<1|1].minn=tree[now].lazy;
tree[now].lazy=0;
}
void dfs1(int now)
{
num[++num[0]]=now;
pos[now]=num[0];
siz[now]=1;
for (int i=first[now];i;i=e[i].next)
if (e[i].v!=fa[now])
{
dep[e[i].v]=dep[now]+1;
fa[e[i].v]=now;
dfs1(e[i].v);
num[++num[0]]=now;
if (siz[e[i].v]>siz[son[now]]) son[now]=e[i].v;
siz[now]+=siz[e[i].v];
}
}
void dfs2(int now,int tp)
{
top[now]=tp;
L[now]=++cnt;
pre[cnt]=now;
if (son[now]) dfs2(son[now],tp);
for (int i=first[now];i;i=e[i].next)
if (e[i].v!=son[now]&&e[i].v!=fa[now])
dfs2(e[i].v,e[i].v);
R[now]=cnt;
}
void init()//RMQ预处理
{
int x,y;
for (int i=1;i<=num[0];i++) f[i][0]=num[i];
for (int i=1;i<=log2(num[0]);i++)
for (int j=1;j<=num[0]-(1<<i);j++)
{
x=f[j][i-1];y=f[j+(1<<(i-1))][i-1];
if (dep[x]>dep[y]) f[j][i]=y;
else f[j][i]=x;
}
}
void build(int now,int begin,int end)
{
if (begin==end){tree[now].minn=def[pre[end]];return;}
int mid=(begin+end)>>1;
build(now<<1,begin,mid);
build(now<<1|1,mid+1,end);
tree[now].minn=min(tree[now<<1].minn,tree[now<<1|1].minn);
}
void update(int now,int begin,int end,int l,int r)
{
if (l<=begin&&end<=r) {tree[now].minn=tree[now].lazy=z;return;}
pushdown(now);
int mid=(begin+end)>>1;
if (mid>=l) update(now<<1,begin,mid,l,r);
if (mid<r) update(now<<1|1,mid+1,end,l,r);
tree[now].minn=min(tree[now<<1].minn,tree[now<<1|1].minn);
}
int get(int now,int begin,int end,int l,int r)
{
if (l<=begin&&end<=r) return tree[now].minn;
pushdown(now);
int mid=(begin+end)>>1,ans=Inf;
if (mid>=l) ans=min(ans,get(now<<1,begin,mid,l,r));
if (mid<r) ans=min(ans,get(now<<1|1,mid+1,end,l,r));
tree[now].minn=min(tree[now<<1].minn,tree[now<<1|1].minn);
return ans;
}
int lca(int x,int y)
{
int pos1=pos[x],pos2=pos[y];
if (pos1>pos2) swap(pos1,pos2);
int mid=log2(pos2-pos1+1);
x=f[pos1][mid];y=f[pos2-(1<<mid)+1][mid];
if (dep[y]>dep[x]) return x;
else return y;
}
void solve(int l,int r)
{
int f1=top[l],f2=top[r];
while (f1!=f2)
{
if (dep[f1]<dep[f2]) swap(l,r),swap(f1,f2);
update(1,1,cnt,L[f1],L[l]);
l=fa[f1];f1=top[l];
}
if (dep[l]>dep[r]) swap(l,r);
update(1,1,cnt,L[l],L[r]);
}
main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<n;i++)
scanf("%d%d",&x,&y),
add(x,y),add(y,x);
for (int i=1;i<=n;i++) scanf("%d",&def[i]);
scanf("%d",&root);
dfs1(1);
dfs2(1,1);
init();
build(1,1,cnt);
while (m--)
{
scanf("%d",&opt);
if (opt==1) scanf("%d",&root);
else if (opt==2) scanf("%d%d%d",&x,&y,&z),solve(x,y);
else
{
scanf("%d",&x);
if (x==root) printf("%d\n",tree[1].minn);
else if (lca(x,root)!=x) printf("%d\n",get(1,1,cnt,L[x],R[x]));
else
{
int ans=Inf;
for (int i=first[x];i;i=e[i].next)
if(L[e[i].v]<=L[root]&&R[e[i].v]>=R[root])
{
z=e[i].v;
break;
}
if (L[z]>1) ans=min(ans,get(1,1,cnt,1,L[z]-1));
if (R[z]<n) ans=min(ans,get(1,1,cnt,R[z]+1,n));
printf("%d\n",ans);
}
}
}
}