因为今天是植树节,所以我们要剖树
题目:
题解:
可以发现除了这个换根操作之外别的全是裸树剖+dfs序维护子树
换根的情况怎么办呢?我们可以分类讨论一下,假如说这次要查询x的子树
- root=x,那么查询的范围是整棵树
- root不在x的子树里,那么对x的查询范围并没有什么影响,还是in~out
- root在x的子树里,那么需要查询的部分是除了通往root这一支之外的最小值
有一个小细节要注意,判断以上的第三种情况时,不能用【除了子树里有root的v[i],别的都加上】,而是用【把这一支找出来然后求补集】,因为如果把其他的都加上,如果遇到fa[x]就很麻烦了,你加还是不加?加的话就白分类了,不加就少了一支,所以要用补集法
代码:
#include <cstdio>
#include <iostream>
#define INF 2147483647
using namespace std;
const int N=100005;
int tot,nxt[N*2],point[N],v[N*2],fa[N],h[N],in[N],out[N],top[N];
int n,delta[N*4],son[N],minn[N*4],root,size[N],totw,a[N],tree[N];
void addline(int x,int y)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
void dfs_1(int x,int faa)
{
fa[x]=faa;h[x]=h[faa]+1;size[x]=1;int maxx=0;
for (int i=point[x];i;i=nxt[i])
if (v[i]!=faa)
{
dfs_1(v[i],x);
size[x]+=size[v[i]];
if (maxx<size[v[i]]) maxx=size[v[i]],son[x]=v[i];
}
}
void dfs_2(int x,int fa)
{
if (son[fa]!=x) top[x]=x;
else top[x]=top[fa];
in[x]=++totw;
if (son[x])
{
dfs_2(son[x],x);
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa && v[i]!=son[x]) dfs_2(v[i],x);
}
out[x]=totw;
}
void updata(int now){minn[now]=min(minn[now<<1],minn[now<<1|1]);}
void pushdown(int now)
{
if (delta[now])
{
delta[now<<1]=delta[now];
delta[now<<1|1]=delta[now];
minn[now<<1]=delta[now];
minn[now<<1|1]=delta[now];
delta[now]=0;
}
}
void build(int now,int l,int r)
{
if (l==r)
{
minn[now]=a[tree[l]];
return;
}
int mid=(l+r)>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
updata(now);
}
void change(int now,int l,int r,int lrange,int rrange,int vv)
{
if (lrange<=l && rrange>=r) {delta[now]=vv; minn[now]=vv;return;}
int mid=(l+r)>>1;
pushdown(now);
if (lrange<=mid) change(now<<1,l,mid,lrange,rrange,vv);
if (rrange>mid) change(now<<1|1,mid+1,r,lrange,rrange,vv);
updata(now);
}
int qurry(int now,int l,int r,int lrange,int rrange)
{
if (lrange<=l && rrange>=r) return minn[now];
int mid=(l+r)>>1,ans=INF;
pushdown(now);
if (lrange<=mid) ans=min(ans,qurry(now<<1,l,mid,lrange,rrange));
if (rrange>mid) ans=min(ans,qurry(now<<1|1,mid+1,r,lrange,rrange));
return ans;
}
void Chan(int u,int v,int vv)
{
int f1=top[u],f2=top[v];
while (f1!=f2)
{
if (h[f1]<h[f2]) swap(f1,f2),swap(u,v);
change(1,1,n,in[f1],in[u],vv);
u=fa[f1]; f1=top[u];
}
if (in[u]>in[v]) swap(u,v);
change(1,1,n,in[u],in[v],vv);
}
int Qu(int x)
{
if (root==x) return qurry(1,1,n,1,n);
if (in[root]<in[x] || in[root]>out[x]) return qurry(1,1,n,in[x],out[x]); //root不在x的子树里
int Min=INF;
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa[x] && in[root]>=in[v[i]] && in[root]<=out[v[i]]) //root在v[i]的子树里
{
if (in[v[i]]>1) Min=min(Min,qurry(1,1,n,1,in[v[i]]-1));//转换为补集一次求解
if (out[v[i]]<n) Min=min(Min,qurry(1,1,n,out[v[i]]+1,n));
}
return Min;
}
int main()
{
int m;scanf("%d%d",&n,&m);
for (int i=1;i<n;i++)
{
int x,y;scanf("%d%d",&x,&y);
addline(x,y);
}
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
scanf("%d",&root);
dfs_1(root,0); dfs_2(root,0);
for (int i=1;i<=n;i++) tree[in[i]]=i;
build(1,1,n);
while (m--)
{
int opt,x,y,v;scanf("%d",&opt);
switch(opt)
{
case 1:scanf("%d",&root);break;
case 2:scanf("%d%d%d",&x,&y,&v);Chan(x,y,v);break;
case 3:scanf("%d",&x);printf("%d\n",Qu(x));break;
}
}
}