首页 > 编程学习 > P3979 遥远的国度

P3979 遥远的国度

发布时间:2022/8/31 19:39:09

题目链接

  如果抛开换根的操作话,就是一个很简单的树链剖分的模板题。但是加了换根之后就边的复杂了起来。首先我们知道,肯定不能每一次换根之后都重新将树剖分一次,所以我们需要去找到换根之后的\(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
	//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;
	}
Copyright © 2010-2022 mfbz.cn 版权所有 |关于我们| 联系方式|豫ICP备15888888号