P3979 遥远的国度

题目链接

  如果抛开换根的操作话,就是一个很简单的树链剖分的模板题。但是加了换根之后就边的复杂了起来。首先我们知道,肯定不能每一次换根之后都重新将树剖分一次,所以我们需要去找到换根之后的root和我们要去查询的节点之间的关系。
  一个很不错的博客
  首先来考虑一下如果换根之后,我们要查询的节点u刚好就是根,那就没有任何的影响,直接查询整棵树的最小值就可以了。
  接着来考虑如果u不在1root的链中,它们之间还是不会产生影响,返回的就是u的子树中的最小值。
  最后考虑u1root的链中就是最特殊的情况了。我们需要将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 //head 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__

本文作者HoneyGrey
本文链接https://www.cnblogs.com/Haven-/p/16644297.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   浅渊  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示