遥远的国度有n个城市,这些城市之间由一些路连接且这些城市构成了一颗树。这个国度有一个首都,我们可以把这个首都看做整棵树的根,但遥远的国度比较奇怪,首都是随时有可能变为另外一个城市的。遥远的国度的每个城市有一个防御值,有些时候RapiD会使得某两个城市之间的路径上的所有城市的防御值都变为某个值。RapiD想知道在某个时候,如果把首都看做整棵树的根的话,那么以某个城市为根的子树的所有城市的防御值最小是多少。由于RapiD无法解决这个问题,所以他拦住了zcwwzdjn希望他能帮忙。但zcwwzdjn还要追杀sb的zhx,所以这个重大的问题就被转交到了你的手上。
这道题跟普通的树链剖分基本没什么区别,但是多了一个改根操作,这个一开始有一点吓到我了。但其实没有那么复杂,我们发现改根之后,对修改操作没有影响,直接做就可以了。可询问操作就要分情况讨论。
如果当前的根等于询问操作的x的话,那其实就是把全部的点给问一次。
如果当前的根跟x的lca不等于x,那其实是对x的子树是没有影响的,照常找它的子树就可以了。
但是如果lca为x,那就说明当前的根原本为x的子节点,那x的实际上的子树就会发生变化,但其实也没有多大变化,只要不查找当前的根所在的链(这条链的起始点为x的其中一个儿子),其他全部查询就可以了,不是很难想。
那这道题就解决了。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
struct node
{
int x,y,next;
}a[210000];int len,last[110000];
void ins(int x,int y)
{
len++;
a[len].x=x;a[len].y=y;
a[len].next=last[x];last[x]=len;
}
struct trnode
{
int l,r,lc,rc,c,lazy;
}tr[210000];int trlen;
void bt(int l,int r)
{
trlen++;int now=trlen;
tr[now].l=l;tr[now].r=r;
tr[now].lc=tr[now].rc=-1;
if(l<r)
{
int mid=(l+r)/2;
tr[now].lc=trlen+1;bt(l,mid);
tr[now].rc=trlen+1;bt(mid+1,r);
}
}
int son[110000],tot[110000],dep[110000],f[110000][20];
void pre_tree_node(int x)
{
for(int i=1;(1<<i)<=dep[x];i++)f[x][i]=f[f[x][i-1]][i-1];
son[x]=0;tot[x]=1;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=f[x][0])
{
f[y][0]=x;
dep[y]=dep[x]+1;
pre_tree_node(y);
if(tot[son[x]]<tot[y])son[x]=y;
tot[x]+=tot[y];
}
}
}
int id,ys[110000],top[110000];
void pre_tree_edge(int x,int tp)
{
ys[x]=++id;top[x]=tp;
if(son[x]!=0)pre_tree_edge(son[x],tp);
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(y!=f[x][0] && y!=son[x])pre_tree_edge(y,y);
}
}
void update(int now)
{
int lc=tr[now].lc,rc=tr[now].rc;
tr[lc].c=tr[now].lazy;tr[lc].lazy=tr[now].lazy;
tr[rc].c=tr[now].lazy;tr[rc].lazy=tr[now].lazy;
tr[now].lazy=0;
}
void change(int now,int l,int r,int k)
{
if(tr[now].l==l && tr[now].r==r){tr[now].c=tr[now].lazy=k;return ;}
if(tr[now].lazy!=0)update(now);
int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/2;
if(r<=mid)change(lc,l,r,k);
else if(mid+1<=l)change(rc,l,r,k);
else change(lc,l,mid,k),change(rc,mid+1,r,k);
tr[now].c=min(tr[lc].c,tr[rc].c);
}
int findmin(int now,int l,int r)
{
if(tr[now].l==l && tr[now].r==r)return tr[now].c;
if(tr[now].lazy!=0)update(now);
int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)/2;
if(r<=mid)return findmin(lc,l,r);
else if(mid+1<=l)return findmin(rc,l,r);
else return min(findmin(lc,l,mid),findmin(rc,mid+1,r));
}
void solve(int x,int y,int k)
{
int tx=top[x],ty=top[y];
while(tx!=ty)
{
if(dep[tx]>dep[ty])swap(tx,ty),swap(x,y);
change(1,ys[ty],ys[y],k);
y=f[ty][0];ty=top[y];
}
if(dep[x]>dep[y])swap(x,y);
change(1,ys[x],ys[y],k);
}
int hehe(int x,int tt)
{
for(int i=17;i>=0;i--)
{
if(tt>=(1<<i))x=f[x][i],tt-=(1<<i);
}
return x;
}
int LCA(int x,int y)
{
if(dep[x]<dep[y])swap(x,y);
for(int i=17;i>=0;i--)
{
if((dep[x]-dep[y])>=(1<<i))x=f[x][i];
}
if(x==y)return x;
for(int i=17;i>=0;i--)
{
if(dep[x]>=(1<<i) && f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
}
return f[x][0];
}
int wy[110000];
int main()
{
int n,m,rt;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
for(int i=1;i<=n;i++)scanf("%d",&wy[i]);
scanf("%d",&rt);
pre_tree_node(rt);pre_tree_edge(rt,rt);bt(1,n);
for(int i=1;i<=n;i++)change(1,ys[i],ys[i],wy[i]);
while(m--)
{
int opt,x,y,c;
scanf("%d%d",&opt,&x);
if(opt==1)rt=x;
if(opt==3)
{
if(rt==x)printf("%d\n",findmin(1,1,n));
else
{
if(LCA(rt,x)==x)
{
int yw=hehe(rt,dep[rt]-dep[x]-1);
int ans=findmin(1,1,ys[yw]-1);
if(ys[yw]+tot[yw]<=n)ans=min(ans,findmin(1,ys[yw]+tot[yw],n));
printf("%d\n",ans);
}
else printf("%d\n",findmin(1,ys[x],ys[x]+tot[x]-1));
}
}
if(opt==2)
{
scanf("%d%d",&y,&c);
solve(x,y,c);
}
}
return 0;
}