LG-P3979 遥远的国度

9 篇文章 0 订阅

树链剖分 换根操作

三个操作:

  1. 修改根;

  2. 修改路径上的值;

  3. 查询以 x x x 为根的子树。


1 修改根:

用变量 r o o t root root 把它存下来即可,它主要是为操作 3 服务的。

d f s dfs dfs 的时候以 1 为根。

2 修改路径上的值:

基本上是常规操作,线段树存两个变量: l z lz lz m i n n minn minn

m i n n minn minn 为 3 服务,不多说, l z lz lz 懒标记主要用于判断 p u s h d o w n pushdown pushdown ,所以值什么的随便赋就好,但一定要有。

3 查询子树最小值:

换根操作。

这个就是这道题的重头戏了。

把树看成一幅图,每次我们把 r o o t root root 作为根,把这个图提起来,然后求以 x x x 为根的子树的最小值。

这是抽象想法,但是我们在查询的时候是按照以 1 为根的子树查询的。

此时大致有 3 种情况:

  1. x = = r o o t x == root x==root :这就是全局最小值。

  2. x x x r o o t root root 没有交集:直接正常查询就好。

  3. x x x 在 1 到 r o o t root root 的链上:抽象想象一下先。如何判断?另写函数算 x 是 root 的几倍祖先或者通过他们的深度判断。如何计算?概述起来就是除了 x 到 root 那条链上的数不要,其他的都要。这里抽象想想可以想通,具体怎么写看代码。

精华代码(换根操作):

int gf (int x, int y)
{
	for (int i = 25; i >= 0; i--)
	{
		if (y >= (1 << i))
		{
			x = f[x][i];
			y -= (1 << i);
		}
	}
	return x;
}
int rquery (int x)
{
	if (x == root) return t[1].minn;
	int y = gf (root, dep[root] - dep[x] - 1);
	if (dep[x] >= dep[root] or f[y][0] != x) return query (1, id[x], id[x] + siz[x] - 1);
	int aa, bb;
	aa = query (1, 1, id[y] - 1);
	bb = query (1, id[y] + siz[y], n);
	return min (aa, bb);
}

感性理解,实在不行去看第一篇题解,就可以看懂了 (我在干什么)

查看题解

AC 代码 (这不压行我都对不起我自己啊)

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 100005;
int dep[maxn], son[maxn], f[maxn][25], siz[maxn], val[maxn], valn[maxn], top[maxn], id[maxn], hd[maxn], cnt, tmp, n, m, ans, root;
struct node{int to, nxt;}e[maxn * 2];
struct tree{int l, r, lz, minn;}t[maxn * 4];
void add (int u, int v){e[++cnt].to = v, e[cnt].nxt = hd[u], hd[u] = cnt;}

void dfs1 (int u, int fa){
	dep[u] = dep[fa] + 1, siz[u] = 1, f[u][0] = fa;
	for (int i = 1; i <= 25; i++) {f[u][i] = f[f[u][i - 1]][i - 1]; if (!f[u][i]) break;}
	int maxss = -1;
	for (int i = hd[u]; i; i = e[i].nxt){
		if (e[i].to == fa) continue;
		dfs1 (e[i].to, u), siz[u] += siz[e[i].to];
		if (siz[e[i].to] > maxss) maxss = siz[e[i].to], son[u] = e[i].to;}}
		
void dfs2 (int x, int tpx){
	top[x] = tpx, id[x] = ++tmp, valn[id[x]] = val[x];
	if (!son[x]) return;
	dfs2 (son[x], tpx);
	for (int i = hd[x]; i; i = e[i].nxt){
		if (e[i].to == son[x] or e[i].to == f[x][0]) continue;
		dfs2 (e[i].to, e[i].to);}}
		
void push_down (int i){if (!t[i].lz) return; t[i << 1].lz = t[i << 1 | 1].lz = t[i].lz, t[i << 1].minn = t[i << 1 | 1].minn = t[i].minn, t[i].lz = 0;}

void build (int i, int l, int r){
	t[i].l = l, t[i].r = r;
	if (l == r) {t[i].minn = valn[l]; return;}
	int mid = (l + r) >> 1;
	build (i << 1, l, mid), build (i << 1 | 1, mid + 1, r);
	t[i].minn = min (t[i << 1].minn, t[i << 1 | 1].minn);}

void update (int i, int l, int r, int x){
	if (t[i].l > r or t[i].r < l) return;
	if (t[i].l >= l and t[i].r <= r){t[i].minn = x, t[i].lz = 1; return;}	
	push_down (i);
	update (i << 1, l, r, x), update (i << 1 | 1, l, r, x);
	t[i].minn = min (t[i << 1].minn, t[i << 1 | 1].minn); } 

int query (int i, int l, int r){
	if (t[i].l > r or t[i].r < l) return 1e18;
	if (t[i].l >= l and t[i].r <= r) return t[i].minn;
	push_down (i);
	return min (query (i << 1, l, r), query (i << 1 | 1, l, r));}

int gf (int x, int y){
	for (int i = 25; i >= 0; i--) if (y >= (1 << i)) x = f[x][i], y -= (1 << i);
	return x;}

void rupdate (int x, int y, int z){
	while (top[x] != top[y]){
		if (dep[top[x]] < dep[top[y]]) swap (x, y);
		update (1, id[top[x]], id[x], z);
		x = f[top[x]][0];}
	if (dep[x] > dep[y]) swap (x, y);
	update (1, id[x], id[y], z);}

int rquery (int x){
	if (x == root) return t[1].minn;
	int y = gf (root, dep[root] - dep[x] - 1);
	if (dep[x] >= dep[root] or f[y][0] != x) return query (1, id[x], id[x] + siz[x] - 1);
	int aa, bb;
	aa = query (1, 1, id[y] - 1), bb = query (1, id[y] + siz[y], n);
	return min (aa, bb);}

signed main (){
	scanf ("%lld %lld", &n, &m);
	for (int i = 1; i < n; i++){
		int u, v;
		scanf ("%lld %lld", &u, &v);
		add (u, v), add (v, u);}
	for (int i = 1; i <= n; i++) scanf ("%lld", &val[i]);
	dfs1 (1, 0), dfs2 (1, 1), build (1, 1, n);
	scanf ("%lld", &root);
	while (m --){
		int op, x, y, z;
		scanf ("%lld", &op);
		if (op == 1) scanf ("%lld", &root);
		if (op == 2) scanf ("%lld %lld %lld", &x, &y, &z), rupdate (x, y, z);
		if (op == 3)scanf ("%lld", &x), printf ("%lld\n", rquery (x));}}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值