P3979 遥远的国度 (树链剖分)

题目链接: 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;
}

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值