如果抛开换根的操作话,就是一个很简单的树链剖分的模板题。但是加了换根之后就边的复杂了起来。首先我们知道,肯定不能每一次换根之后都重新将树剖分一次,所以我们需要去找到换根之后的root和我们要去查询的节点之间的关系。
一个很不错的博客
首先来考虑一下如果换根之后,我们要查询的节点u刚好就是根,那就没有任何的影响,直接查询整棵树的最小值就可以了。
接着来考虑如果u不在1到root的链中,它们之间还是不会产生影响,返回的就是u的子树中的最小值。
最后考虑u在1到root的链中就是最特殊的情况了。我们需要将u这棵树扣出来,计算剩下两个分出来的子树的最小值
#include <bits/stdc++.h>
using i64 = long long;
#define rep(i,a,n) for (int i = a; i < n; i ++ )
#define per(i,a,n) for (int i = n; i >= a; i -- )
#define all(v) v.begin(), v.end()
#define SZ(s) int(s.size())
#define pb push_back
#define fi first
#define se second
constexpr int N = 100010;
constexpr int inf = std::numeric_limits<int>::max();
int a[N], rnk[N];
struct Tag {
int tag1;
};
struct Info {
int minv;
};
Info operator + (const Info& a, const Info& b) {
Info c;
c.minv = std::min(a.minv, b.minv);
return c;
}
struct Node {
Info val;
Tag tag;
} tr[N << 2];
void build(int u, int l, int r) {
if (l == r) {
tr[u].val.minv = a[rnk[l]];
return ;
}
int mid = (l + r) >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
tr[u].val = tr[u << 1].val + tr[u << 1 | 1].val;
}
void settag(int u, int v) {
tr[u].val.minv = v;
tr[u].tag.tag1 = v;
}
void push(int u) {
if (tr[u].tag.tag1) {
settag(u << 1, tr[u].tag.tag1);
settag(u << 1 | 1, tr[u].tag.tag1);
tr[u].tag.tag1 = 0;
}
}
void change(int u, int l, int r, int ln, int rn, int v) {
if (l >= ln && r <= rn) return void(settag(u, v));
push(u);
int mid = (l + r) >> 1;
if (mid >= ln) change(u << 1, l, mid, ln, rn, v);
if (mid < rn) change(u << 1 | 1, mid + 1, r, ln, rn, v);
tr[u].val = tr[u << 1].val + tr[u << 1 | 1].val;
}
int query(int u, int l, int r, int ln, int rn) {
if (l >= ln && r <= rn) return tr[u].val.minv;
int mid = (l + r) >> 1;
push(u);
int ans = inf;
if (mid >= ln) ans = std::min(ans, query(u << 1, l, mid, ln, rn)) ;
if (mid < rn) ans = std::min(ans, query(u << 1 | 1, mid + 1, r, ln, rn));
return ans;
}
signed main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n, m;
std::cin >> n >> m;
std::vector<std::vector<int>> adj(n + 1);
for (int i = 1; i < n; i++) {
int u, v;
std::cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
std::vector<int> parent(n + 1), siz(n + 1), son(n + 1), dep(n + 1);
std::vector<int> dfn(n + 1), top(n + 1);
int idx = 0;
for (int i = 1; i <= n; i++) std::cin >> a[i];
std::function<void(int, int, int)> dfs1 = [&] (int u, int fa, int depth) {
parent[u] = fa, dep[u] = depth, siz[u] = 1;
for (auto v : adj[u]) {
if (v == fa) continue;
dfs1(v, u, depth + 1);
siz[u] += siz[v];
if (siz[v] > siz[son[u]])
son[u] = v;
}
};
std::function<void(int, int)> dfs2 = [&] (int u, int t) -> void {
dfn[u] = ++ idx, top[u] = t, rnk[idx] = u;
if (!son[u]) return ;
dfs2(son[u], t);
for (auto& v : adj[u]) {
if (v == parent[u] || v == son[u]) continue;
dfs2(v, v);
}
};
auto update = [&] (int u, int v, int x) -> void {
while(top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) std::swap(u, v);
change(1, 1, n, dfn[top[u]], dfn[u], x);
u = parent[top[u]];
}
if (dep[u] > dep[v]) std::swap(u, v);
change(1, 1, n, dfn[u], dfn[v], x);
};
dfs1(1, 0, 1);
dfs2(1, 1);
int root;
std::cin >> root;
build(1, 1, n);
auto check = [&] (int u) -> int {
if (u == root) return -1;
if (dfn[u] >= dfn[root] || dfn[u] + siz[u] - 1 < dfn[root]) return 0;
int now = root;
while(top[now] != top[u]) {
if (parent[top[now]] == u) return top[now];
now = parent[top[now]];
}
return son[u];
};
auto ask = [&] (int u) -> int {
int now = check(u);
if (now == -1) return tr[1].val.minv;
if (now == 0) {
return query(1, 1, n, dfn[u], dfn[u] + siz[u] - 1);
}
int ans = query(1, 1, n, 1, dfn[now] - 1);
if (dfn[now] + siz[now] - 1 != n) ans = std::min(ans, query(1, 1, n, dfn[now] + siz[now], n));
return ans;
};
for (int i = 0; i < m; i++) {
int op;
std::cin >> op;
if (op == 1) {
std::cin >> root;
} else if (op == 2) {
int x, y, d;
std::cin >> x >> y >> d;
update(x, y, d);
} else {
int p;
std::cin >> p;
std::cout << ask(p) << "\n";
}
}
return 0 ^ 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下