题目描述
题解
难点在于如何处理换根操作。我们可以分情况讨论。假设当前根节点为 r o o t root root,询问子树为 s o n 。 son。 son。
- 当 x x x不在 r o o t root root到 1 1 1的路径中时,我们发现答案不会产生变化,直接输出x的子树中的最小值即可。在这里,我们可以用树的DFS序的最小值即可。
- 如果在这条路径中,我们发现答案就是除了这条链上x的儿子所在子树的所有节点的最小值。即 r o o t root root向上找 d e e p r o o t − d e e p x − 1 deep_{root}-deep_x-1 deeproot−deepx−1次父亲的结果。这个操作我们可以用树上倍增来实现,一开始预处理出倍增数组即可。这与这个节点 y y y而言,这个点所在子树入股偶在 d f s dfs dfs序上是 L y , R y L_y,R_y Ly,Ry,那么答案就是 m i n ( [ 1 , L y − 1 ] , [ R y + 1 , n ] ) . min([1,L_y-1],[R_y+1,n]). min([1,Ly−1],[Ry+1,n]).
然后就是比较模板的树链剖分了。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200000;
int n, m, tot = 0;
int dfn[N], L[N], R[N], dep[N];
int f[N][25], size[N], son[N], top[N], val[N];
vector <int> G[N];
struct seg {
int l, r, sum, min, tag;
} a[N*10];
void dfs1(int x,int fa)
{
size[x] = 1, dep[x] = dep[fa]+1;
for (int i=0,y;i<G[x].size();++i)
{
if ((y = G[x][i]) == fa) continue;
f[y][0] = x, dfs1(y,x), size[x] += size[y];
if (size[y] > size[son[x]]) son[x] = y;
}
return;
}
void dfs2(int x,int cur)
{
dfn[L[x] = ++tot] = x, top[x] = cur;
if (son[x] > 0) dfs2(son[x],cur);
for (int i=0,y;i<G[x].size();++i)
{
y = G[x][i];
if (y != f[x][0] && y != son[x]) dfs2(y,y);
}
R[x] = tot;
return;
}
void DP(void)
{
for (int j=1;j<=23;++j)
for (int i=1;i<=n;++i)
f[i][j] = f[f[i][j-1]][j-1];
return;
}
void build(int p,int l,int r)
{
a[p].l = l, a[p].r = r, a[p].min = LONG_LONG_MAX;
if (l == r) {a[p].min = val[dfn[l]]; return;}
int mid = l+r >> 1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
a[p].min = min(a[p*2].min,a[p*2+1].min);
return;
}
void spread(int p)
{
if (a[p].tag == 0) return;
a[p*2].tag = a[p*2+1].tag = a[p].tag;
a[p*2].min = a[p*2+1].min = a[p].tag;
a[p].tag = 0;
return;
}
void change(int p,int l,int r,int v)
{
if (l <= a[p].l && r >= a[p].r) {
a[p].tag = a[p].min = v;
return;
}
spread(p);
int mid = a[p].l+a[p].r >> 1;
if (l <= mid) change(p*2,l,r,v);
if (r > mid) change(p*2+1,l,r,v);
a[p].min = min(a[p*2].min,a[p*2+1].min);
return;
}
int ask(int p,int l,int r)
{
if (l <= a[p].l && r >= a[p].r) return a[p].min;
spread(p);
int mid = a[p].l+a[p].r >> 1, res = LONG_LONG_MAX;
if (l <= mid) res = min(res,ask(p*2,l,r));
if (r > mid) res = min(res,ask(p*2+1,l,r));
return res;
}
void modify(int x,int y,int v)
{
for (; top[x] ^ top[y]; x = f[top[x]][0])
{
if (dep[top[x]] < dep[top[y]])
x ^= y ^= x ^= y;
change(1,L[top[x]],L[x],v);
}
if (dep[x] > dep[y]) x ^= y ^= x ^= y;
change(1,L[x],L[y],v);
return;
}
int getfa(int x,int y)
{
for (int i=23;i>=0;--i)
if ((y >> i) & 1) x = f[x][i];
return x;
}
signed main(void)
{
scanf("%lld %lld", &n, &m);
for (int i=1,x,y;i<n;++i)
{
scanf("%lld %lld", &x, &y);
G[x].push_back(y);
G[y].push_back(x);
}
for (int i=1;i<=n;++i)
scanf("%lld", val+i);
dfs1(1,0), dfs2(1,1), DP();
build(1,1,n);
int root; scanf("%lld", &root);
while (m --)
{
int opt; scanf("%lld", &opt);
if (opt == 1) scanf("%lld", &root);
if (opt == 2) {
int x, y, v; scanf("%lld %lld %lld", &x, &y, &v);
modify(x,y,v);
}
if (opt == 3) {
int x, y, res = LONG_LONG_MAX; scanf("%lld", &x);
if (x == root) {
printf("%lld\n", a[1].min);
continue;
}
if (dep[root] > dep[x] && x == f[ y = getfa(root,dep[root]-dep[x]-1) ][0]) {
if (L[y] > 1) res = min(res,ask(1,1,L[y]-1));
if (R[y] < n) res = min(res,ask(1,R[y]+1,n));
printf("%lld\n", res);
}
else printf("%lld\n", ask(1,L[x],R[x]));
}
}
return 0;
}