抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

题目传送门

题意

给定一棵有点权的树和初始的根,支持三种操作:换根,修改路径上的权值,查询子树权值\(min\)

题解

如果没有换根,这就是树剖+线段树裸题了。

那么有换根呢?首先我们一开始认定初始给定的根作为根进行树剖。

换根之后,路径修改显然不产生影响,直接用线段树维护就好了。假设现在的根为\(rt\),查询子树的根节点为\(x\),分以下三种情况(可以脑补一下或者画个图):

  1. \(rt=x\)
  2. \(rt\)\(x\)的子树外
  3. \(rt\)\(x\)的子树内(不包括\(x\)

注意这里的子树是相对于以初始根为根时的树而言的。

对于1,相当于查询整棵树的min,直接在整棵线段树上查询就好了。

对于2,又有两种情况:一种是\(x\)\(rt\)的子树内(不包括\(rt\)),另一种是\(x\)的子树和\(rt\)的子树互不包含。

画一下图即可知道,这两种情况是一样的,即不需要考虑换根,换根之后\(x\)子树中的节点没有变化,直接查询以初始根为根时\(x\)的子树min。

对于3,就有点麻烦了。我们设\(u\)\(x\)\(rt\)这条链上除\(x\)外的第二个点(\(x\)的儿子)。那么在纸上画一下即可得知,换根后的\(x\)的子树变为整棵树减去换根前\(u\)的子树(注意不是\(x\)的子树减去\(u\)的子树)。

那么问题在于求u。算法描述如下(设树剖后节点\(i\)所在重链的顶端节点为\(top[i]\),节点\(i\)的父亲为\(fa[i]\),节点\(i\)的重儿子是\(son[i]\)):

  1. \(u\gets rt\)
  2. 如果\(fa[top[u]]\)\(x\)的子树中并且不等于\(x\),那么\(u\gets fa[top[u]]\)。不断执行这一过程直至条件不成立。
  3. 如果\(fa[top[u]]=x\),那么\(u=top[u]\),否则\(u=son[x]\)

解释一下第三步,因为最后如果\(fa[top[u]]=x\)那么\(top[u]\)刚好就是这条链上\(x\)的儿子了,否则说明\(fa[top[u]]\)\(x\)的祖先,即\(x\)\(u\)在同一条重链上,那么这条链上\(x\)的儿子就是\(x\)的重儿子。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/**************************************************************
Problem: 3083
User: rill7747
Language: C++
Result: Accepted
Time:3312 ms
Memory:13452 kb
****************************************************************/

#include <cstdio>
#include <cctype>
#include <algorithm>
int read(){
register int x = 0;
register char f = 1, ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = 0;
for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ '0');
return f ? x : -x;
}
#define N 100005
int n, m, a[N], rt;
int edge, to[N << 1], pr[N << 1], hd[N];
void addedge(int u, int v){
to[++edge] = v, pr[edge] = hd[u], hd[u] = edge;
}
int dep[N], fa[N], sz[N], son[N];
void dfs(int u){
sz[u] = 1;
for (register int i = hd[u], v; i; i = pr[i])
if ((v = to[i]) != fa[u])
fa[v] = u, dep[v] = dep[u] + 1, dfs(v), sz[u] += sz[v],
!son[u] || sz[v] > sz[son[u]] ? son[u] = v : 0;
}
int top[N], idx, st[N], id[N], ed[N];
void dfs(int u, int tp){
top[u] = tp, id[st[u] = ++idx] = u;
if (son[u]) dfs(son[u], tp);
for (register int i = hd[u], v; i; i = pr[i])
if ((v = to[i]) != fa[u] && v != son[u]) dfs(v, v);
ed[u] = idx;
}
int val[N << 2], lz[N << 2];
void build(int u, int l, int r){
if (l == r) return val[u] = a[id[l]], lz[u] = 0, void(0);
int mid = (l + r) >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
val[u] = std :: min(val[u << 1], val[u << 1 | 1]);
}
void down(int u){
if (lz[u]) val[u << 1] = val[u << 1 | 1] = lz[u << 1] = lz[u << 1 | 1] = lz[u];
}
void modify(int u, int l, int r, int L, int R, int v){
if (L <= l && r <= R) return val[u] = lz[u] = v, void(0);
int mid = (l + r) >> 1; down(u);
if (L <= mid) modify(u << 1, l, mid, L, R, v);
if (R > mid) modify(u << 1 | 1, mid + 1, r, L, R, v);
val[u] = std :: min(val[u << 1], val[u << 1 | 1]);
}
int query(int u, int l, int r, int L, int R){
if (L <= l && r <= R) return val[u];
int mid = (l + r) >> 1, ans = 2147483647; down(u);
if (L <= mid) ans = std :: min(ans, query(u << 1, l, mid, L, R));
if (R > mid) ans = std :: min(ans, query(u << 1 | 1, mid + 1, r, L, R));
return ans;
}
void Modify(int u, int v, int w){
while (top[u] != top[v]){
if (dep[top[u]] < dep[top[v]]) std :: swap(u, v);
modify(1, 1, n, st[top[u]], st[u], w), u = fa[top[u]];
}
if (dep[u] < dep[v]) std :: swap(u, v);
modify(1, 1, n, st[v], st[u], w);
}
int Query(int u){
if (u == rt) return val[1];
if (st[rt] < st[u] || st[rt] > ed[u]) return query(1, 1, n, st[u], ed[u]);
int v = rt;
while (st[fa[top[v]]] > st[u] && st[fa[top[v]]] <= ed[u]) v = fa[top[v]];
if (fa[top[v]] == u) v = top[v]; else v = son[u];
return std :: min(query(1, 1, n, 1, st[v] - 1), query(1, 1, n, ed[v] + 1, n));
}
int main(){
n = read(), m = read();
for (register int i = 1, u, v; i < n; ++i)
u = read(), v = read(), addedge(u, v), addedge(v, u);
for (register int i = 1; i <= n; ++i) a[i] = read();
rt = read();
dfs(rt), dfs(rt, rt), build(1, 1, n);
while (m--){
int opt = read(), x, y, w;
if (opt == 1) rt = read();
if (opt == 2) x = read(), y = read(), w = read(), Modify(x, y, w);
if (opt == 3) printf("%d\n", Query(read()));
}
}

评论