题目链接: P3979 遥远的国度
大致题意
给定一棵有 n n n个节点的树, 根节点为 r o o t root root, 有如下三种操作:
1 rt
把根节点修改为
r
t
rt
rt. (即:
r
o
o
t
=
r
t
root = rt
root=rt)
2 a b c
把
a
,
b
a, b
a,b两点间的所有点的权值修改为
c
c
c.
3 x
查询以
r
o
o
t
root
root为根时,
x
x
x子树中的最小值
解题思路
树链剖分
我们如果考虑没有换根操作, 本题实质上就是路径修改 + 子树查询, 我们直接套树链剖分板子即可.
考虑到多了一个换根操作, 于是我们不妨分类讨论:
①
r
o
o
t
root
root不在节点
x
x
x或
x
x
x节点的子树中,
②
r
o
o
t
=
=
x
root == x
root==x.
③
r
o
o
t
root
root位于
x
x
x的子树中.
对于情况①: 我们直接对于
x
x
x所在的子树查询即可.
对于情况②: 我们直接查询整棵树上的最小值即可.
对于情况③: 我们发现答案是: 对于
x
x
x节点而言, 除
r
o
o
t
root
root节点所处的子树之外的所有点.
注: 情况③: 如果 y y y节点是 x x x节点的一个直接儿子, 且 r o o t root root位于 y y y节点的子树中.
那么答案就是除 y y y节点及其子树 之外的所有节点.
因此我们可以把区间断为两部分: [ 1 , i d [ y ] − 1 ] [1, id[y] - 1] [1,id[y]−1] 和 [ i d [ y ] + s z [ y ] , n ] [id[y] + sz[y], n] [id[y]+sz[y],n]
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10, B = 17;
int w[N]; //给定的树中各个顶点的权值
vector<int> edge[N]; //树上各个点之间的边
int p[N], dep[N], sz[N], son[N];
// 父节点 深度 节点大小 重儿子
void dfs1(int x = 1, int fa = 0) { // x = 树根节点
p[x] = fa, dep[x] = dep[fa] + 1, sz[x] = 1; // son[x] = 0;
for (auto& to : edge[x]) {
if (to == fa) continue;
dfs1(to, x);
sz[x] += sz[to]; // 特别的, 如果边权->点权, 应记录w[to] = 边权.
if (sz[to] > sz[son[x]]) son[x] = to; //更新重儿子
}
}
int id[N], nw[N], top[N], ind;
// 新编号 新值 重链顶 当前用到的编号
void dfs2(int x = 1, int tp = 1) { // x = 树根节点, tp = 树根节点
id[x] = ++ind, nw[ind] = w[x], top[x] = tp;
if (!son[x]) return; //叶子结点
dfs2(son[x], tp); //先遍历重儿子
for (auto& to : edge[x]) {
if (to == p[x] or to == son[x]) continue;
dfs2(to, to);
}
}
struct node {
int l, r;
int fmin;
int lazy;
}t[N << 2];
void pushdown(node& op, int lazy) {
op.fmin = lazy;
op.lazy = lazy;
}
void pushdown(int x) {
if (!t[x].lazy) return;
pushdown(t[x << 1], t[x].lazy), pushdown(t[x << 1 | 1], t[x].lazy);
t[x].lazy = 0;
}
void pushup(int x) { t[x].fmin = min(t[x << 1].fmin, t[x << 1 | 1].fmin); }
void build(int l, int r, int x = 1) {
t[x] = { l, r, t[x].fmin = nw[l], 0 };
if (l == r) return;
int mid = l + r >> 1;
build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
pushup(x);
}
void modify(int l, int r, int c, int x = 1) {
if (l <= t[x].l and r >= t[x].r) {
pushdown(t[x], c);
return;
}
pushdown(x);
int mid = t[x].l + t[x].r >> 1;
if (l <= mid) modify(l, r, c, x << 1);
if (r > mid) modify(l, r, c, x << 1 | 1);
pushup(x);
}
int ask(int l, int r, int x = 1) {
if (l <= t[x].l and r >= t[x].r) return t[x].fmin;
pushdown(x);
int mid = t[x].l + t[x].r >> 1;
int res = INT_MAX;
if (l <= mid) res = ask(l, r, x << 1);
if (r > mid) res = min(res, ask(l, r, x << 1 | 1));
return res;
}
void modify_route(int a, int b, int c) {
while (top[a] != top[b]) {
if (dep[top[a]] < dep[top[b]]) swap(a, b);
modify(id[top[a]], id[a], c);
a = p[top[a]];
}
if (dep[a] > dep[b]) swap(a, b);
modify(id[a], id[b], c);
}
bool insubtree(int a, int b) { return id[a] <= id[b] and id[b] <= id[a] + sz[a] - 1; }
int ask_subtree(int x) { return ask(id[x], id[x] + sz[x] - 1); }
/* 倍增LCA部分 */
int f[N][B + 1];
void dfs(int x, int fa) {
f[x][0] = fa;
rep(i, B) f[x][i] = f[f[x][i - 1]][i - 1];
for (auto& to : edge[x]) if (to != fa) dfs(to, x);
}
int jump(int x, int depth) {
for (int i = B; i >= 0; --i) if (depth & (1 << i)) x = f[x][i];
return x;
}
int main()
{
int n, m; cin >> n >> m;
rep(i, n - 1) {
int a, b; scanf("%d %d", &a, &b);
edge[a].push_back(b), edge[b].push_back(a);
}
rep(i, n) scanf("%d", &w[i]);
int root; cin >> root;
dfs1(), dfs2();
build(1, n);
dfs(1, 0);
rep(i, m) {
int tp; scanf("%d", &tp);
if (tp == 1) scanf("%d", &root);
else if (tp == 2) {
int a, b, c; scanf("%d %d %d", &a, &b, &c);
modify_route(a, b, c);
}
else {
int x; scanf("%d", &x);
if (x == root) printf("%d\n", ask(1, n));
else if (!insubtree(x, root)) printf("%d\n", ask_subtree(x));
else {
int res = INT_MAX;
int qaq = jump(root, dep[root] - dep[x] - 1);
if (id[qaq] - 1 >= 1) res = min(res, ask(1, id[qaq] - 1));
if (id[qaq] + sz[qaq] <= n) res = min(res, ask(id[qaq] + sz[qaq], n));
printf("%d\n", res);
}
}
}
return 0;
}