传送门:bzoj3083
题解
这篇题解,必须要写(这道简单题真的坑到我了)…好久没打树剖的下场。
除开换根都是裸树剖+线段树。
以1号节点为根建树。换根可以
O(1)
O
(
1
)
换,只需要在查询的时候讨论一下:
若当前查询的节点
id
i
d
不在1到
root
r
o
o
t
的路径上,直接查询子树信息(
dfs
d
f
s
序上的
in,out
i
n
,
o
u
t
)。
反之,当前以
id
i
d
为根的子树便是整棵树去掉以
x
x
向方向的第一个子节点 为根的子树,跳重链到这个子节点
x′
x
′
即可,
id
i
d
子树
dfs
d
f
s
区间便为
[1,in[x′]−1]∪[out[x′]+1,n]
[
1
,
i
n
[
x
′
]
−
1
]
∪
[
o
u
t
[
x
′
]
+
1
,
n
]
。
这里需要特殊判断一种情况:当
id==root
i
d
==
r
o
o
t
时,子树为
[1,n]
[
1
,
n
]
(不然会WA得很惨TAT)。
注意几点(主要是必须熟练打板):
- 树剖第二遍 dfs d f s 时标记 dfs d f s 序,不是第一遍(会导致重链 dfs d f s 序不连续)
- 情况要讨论完整,多想想特殊情况
- 0<所有权值<=2^31!刚好爆 int i n t !
总之做这道题的体验十分不好TAT,以后要多做DS,锻炼码力QAQ
代码
#include<bits/stdc++.h>
#define gc getchar()
#define si isdigit(c)
#define lc k<<1
#define rc k<<1|1
#define mid (((l)+(r))>>1)
using namespace std;
typedef long long ll;
const int N=1e5+10;
const ll inf=1LL<<33;
int n,m,in[N],ot[N],d[N],rv[N],dfn;
int head[N],to[N<<1],nxt[N<<1],tot;
int rt,son[N],sz[N],top[N],f[N];
ll a[N],mn[N<<2],st[N<<2],ans;
char c;
template<class T>
inline void rd(T &x)
{
c=gc;x=0;
for(;!si;c=gc);
for(;si;c=gc) x=x*10+(c^48);
}
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
inline void dfs(int x)
{
int i,j;
sz[x]=1;
for(i=head[x];i;i=nxt[i]){
j=to[i];if(j==f[x]) continue;
f[j]=x;d[j]=d[x]+1;dfs(j);sz[x]+=sz[j];
if(sz[j]>sz[son[x]]) son[x]=j;
}
}
inline void dfss(int x,int tp)
{
top[x]=tp;in[x]=++dfn;rv[dfn]=x;
if(son[x]) dfss(son[x],tp);
int i,j;
for(i=head[x];i;i=nxt[i]){
j=to[i];if(j==f[x] || j==son[x]) continue;
dfss(j,j);
}
ot[x]=dfn;
}
inline void pushup(int k)
{mn[k]=min(mn[lc],mn[rc]);}
inline void pushdown(int k)
{
if(!st[k]) return;
st[lc]=st[rc]=st[k];
mn[lc]=mn[rc]=st[k];
st[k]=0;
}
inline void build(int k,int l,int r)
{
if(l==r){mn[k]=a[rv[l]];return;}
build(lc,l,mid);build(rc,mid+1,r);
pushup(k);
}
inline void sett(int k,int l,int r,int L,int R,ll vv)
{
if(L<=l && r<=R) {st[k]=mn[k]=vv;return;}
pushdown(k);
if(L<=mid) sett(lc,l,mid,L,R,vv);
if(R>mid) sett(rc,mid+1,r,L,R,vv);
pushup(k);
}
inline ll ask(int k,int l,int r,int L,int R)
{
if(L>R) return inf;
if(L<=l && r<=R) return mn[k];
pushdown(k);ll re=inf;
if(L<=mid) re=ask(lc,l,mid,L,R);
if(R>mid) re=min(re,ask(rc,mid+1,r,L,R));
return re;
}
inline void change(int x,int y,ll z)
{
for(;top[x]!=top[y];x=f[top[x]]){
if(d[top[x]]<d[top[y]]) swap(x,y);
sett(1,1,n,in[top[x]],in[x],z);
}
if(d[x]<d[y]) swap(x,y);
sett(1,1,n,in[y],in[x],z);
}
int main(){
int op,i,x,y;ll z;
rd(n);rd(m);
for(i=1;i<n;++i){rd(x);rd(y);lk(x,y);lk(y,x);}
for(i=1;i<=n;++i) rd(a[i]);
dfs(1);dfss(1,1);
build(1,1,n);
rd(rt);
for(;m;--m){
rd(op);
if(op==1) rd(rt);
else if(op==2){
rd(x);rd(y);rd(z);
change(x,y,z);
}else{
rd(x);
if(x==rt) ans=ask(1,1,n,1,n);
else if((in[x]>=in[rt] && ot[x]<=ot[rt]) || (ot[x]<in[rt]) || (in[x]>ot[rt]))
ans=ask(1,1,n,in[x],ot[x]);
else{
y=rt;
for(;top[y]!=top[x] && f[top[y]]!=x;y=f[top[y]]);
if(top[x]==top[y]) y=son[x];else y=top[y];
ans=min(ask(1,1,n,1,in[y]-1),ask(1,1,n,ot[y]+1,n));
}
printf("%lld\n",ans);
}
}
}