树剖换根_

P3979 遥远的国度

本题需要支持 3 3 3 个操作:

  1. 把根修改为 i d id id
  2. x    y x\;y xy 路径上的所有点权修改为 v v v
  3. 询问以 x x x 为根的子树中的最小点权。

看到这道题,第一思路就是对于每个操作 1 1 1,换根后重新剖一遍。但时间复杂度为 O ⁡ ( q n ) \operatorname{O}(qn) O(qn)直接原地爆炸

所以,我们想到另一种方法,直接以 1 1 1 为根来剖,记录当前的根,每次修改和查询的时候再根据根的位置回答。

对于修改操作,由于一棵树上任意两点间只有一条路径,所以直接修改就行了。

重点是查询:

设当前的根为 r t rt rt,要询问以 x x x 为根的子树中的最小值。

  1. x = r t x=rt x=rt:直接输出全局最小值;
  2. r t rt rt x x x 的祖先:直接输出 x x x 的子树的最小值;
  3. x x x r t rt rt 的祖先:这种情况非常 毒瘤 \color{White}\colorbox{White}{毒瘤} 有意思!我们待会会讨论如何处理;
  4. 以上情况都不是,相当于 x x x 在其他的分支,也是直接输出 x x x 的子树的最小值。

判断顺序:先判断是否是 1 1 1,再判断是否是 3 3 3,剩下自然就是 2 2 2 4 4 4 了。

现在讨论一下如何处理情况 3 3 3

如图,若 x = 2 , r t = 4 x=2,rt=4 x=2,rt=4,则以 4 4 4 为根时, 2 2 2 的子树就是除了往 4 4 4 的方向的节点外的所有节点。也就是要找 x x x r t rt rt 这条路径上 x x x 的儿子。

找的方法可以用倍增,记录 f a ( x ) ( i ) fa(x)(i) fa(x)(i) x x x 的第 2 i 2^i 2i 级祖先, getfa(x,k) ⁡ \operatorname{getfa(x,k)} getfa(x,k) 函数返回 x x x 的第 2 k 2^k 2k 级祖先,实现如下:

int getfa(int x, int k)
{
	for (int i = lg[k]; i >= 0; i--)
	{
		if (k >= (1 << i))
		{
			x = fa[x][i];
			k -= (1 << i);
		}
	}
	return x;
}

y ← getfa(x,dep[rt]-dep[x]-1) ⁡ y\gets\operatorname{getfa(x,dep[rt]-dep[x]-1)} ygetfa(x,dep[rt]-dep[x]-1),我们只需判断 f a ( y ) ( 0 ) fa(y)(0) fa(y)(0) 是否等于 x x x 即可,这样既判断了 x x x 是否是 r t rt rt 的祖先,又顺便求出了 y y y

Code \Large\text{Code} Code

#include <iostream>
#include <cstdio>
using namespace std;

const int MAXN = 1e5 + 5;
const int INF = 0x7fffffff;

int cnt, Time;
int b[MAXN], a[MAXN], head[MAXN], fa[MAXN][18], lg[MAXN], dep[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], top[MAXN];

struct edge
{
	int to, nxt;
}e[MAXN << 1];

void add(int u, int v)
{
	e[++cnt] = edge{v, head[u]};
	head[u] = cnt;
}

void dfs1(int u, int father)
{
	fa[u][0] = father;
	for (int i = 1; i <= 17; i++)
	{
		fa[u][i] = fa[fa[u][i - 1]][i - 1];
	}
	dep[u] = dep[father] + 1;
	siz[u] = 1;
	for (int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (v == father)
		{
			continue;
		}
		dfs1(v, u);
		siz[u] += siz[v];
		if (siz[v] > siz[son[u]])
		{
			son[u] = v;
		}
	}
}

void dfs2(int u, int topp)
{
	dfn[u] = ++Time;
	a[Time] = b[u];
	top[u] = topp;
	if (son[u])
	{
		dfs2(son[u], topp);
	}
	for (int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (top[v])
		{
			continue;
		}
		dfs2(v, v);
	}
}

int getfa(int x, int k)
{
	for (int i = lg[k]; i >= 0; i--)
	{
		if (k >= (1 << i))
		{
			x = fa[x][i];
			k -= (1 << i);
		}
	}
	return x;
}

#define lson pos << 1
#define rson pos << 1 | 1

struct tree
{
	int l, r, val = INF, tag;
}t[MAXN << 2];

void pushup(int pos)
{
	t[pos].val = min(t[lson].val, t[rson].val);
}

void pushdown(int pos)
{
	if (t[pos].tag)
	{
		t[lson].val = t[lson].tag = t[rson].val = t[rson].tag = t[pos].tag;
		t[pos].tag = 0;
	}
}

void build(int pos, int l, int r)
{
	t[pos].l = l, t[pos].r = r;
	if (l == r)
	{
		t[pos].val = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(lson, l, mid);
	build(rson, mid + 1, r);
	pushup(pos);
}

void update(int pos, int L, int R, int k)
{
	int l = t[pos].l, r = t[pos].r;
	if (l >= L && r <= R)
	{
		t[pos].val = t[pos].tag = k;
		return;
	}
	pushdown(pos);
	int mid = (l + r) >> 1;
	if (L <= mid)
	{
		update(lson, L, R, k);
	}
	if (R > mid)
	{
		update(rson, L, R, k);
	}
	pushup(pos);
}

int query(int pos, int L, int R)
{
	int l = t[pos].l, r = t[pos].r;
	if (l >= L && r <= R)
	{
		return t[pos].val;
	}
	pushdown(pos);
	int mid = (l + r) >> 1, res = INF;
	if (L <= mid)
	{
		res = query(lson, L, R);
	}
	if (R > mid)
	{
		res = min(res, query(rson, L, R));
	}
	return res;
}

void update_path(int x, int y, int v)
{
	while (top[x] != top[y])
	{
		if (dep[top[x]] < dep[top[y]])
		{
			swap(x, y);
		}
		update(1, dfn[top[x]], dfn[x], v);
		x = fa[top[x]][0];
	}
	if (dep[x] < dep[y])
	{
		swap(x, y);
	}
	update(1, dfn[y], dfn[x], v);
}

int query_subtree(int x)
{
	return query(1, dfn[x], dfn[x] + siz[x] - 1);
}

int main()
{
	int n, m, u, v, rt, op, x, y;
	scanf("%d%d", &n, &m);
	for (int i = 2; i <= n; i++)
	{
		lg[i] = lg[i >> 1] + 1;
	}
	for (int i = 1; i < n; i++)
	{
		scanf("%d%d", &u, &v);
		add(u, v);
		add(v, u);
	}
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", b + i);
	}
	dfs1(1, 0);
	dfs2(1, 1);
	build(1, 1, n);
	scanf("%d", &rt);
	while (m--)
	{
		scanf("%d", &op);
		if (op == 1)
		{
			scanf("%d", &rt);
		}
		else if (op == 2)
		{
			scanf("%d%d%d", &x, &y, &v);
			update_path(x, y, v);
		}
		else
		{
			scanf("%d", &x);
			if (x == rt)
			{
				printf("%d\n", t[1].val);
			}
			else if (dep[x] < dep[rt] && fa[y = getfa(rt, dep[rt] - dep[x] - 1)][0] == x)
			{
				if (dfn[u] + siz[u] <= n)
				{
					printf("%d\n", min(query(1, 1, dfn[y] - 1), query(1, dfn[y] + siz[y], n))); //y的子树不要
				}
				else
				{
					printf("%d\n", query(1, 1, dfn[y] - 1));
				}
			}
			else
			{
				printf("%d\n", query_subtree(x));
			}
		}
	}
	return 0;
}

CF916E Jamie and Tree

  1. 给定一个点 v v v,将整颗树的根变为 v v v
  2. 给定两个点 u , v u, v u,v,将 l c a ( u , v ) lca(u, v) lca(u,v) 所在的子树都加上 x x x
  3. 给定一个点 v v v,回答以 v v v 所在的子树的权值和。

这道题的操作 1 1 1 3 3 3 都和上一题一样,只是操作 2 2 2 变成了子树修改,所以也要分类讨论:

  1. l c a = r t lca=rt lca=rt:直接将全局和加上 x x x
  2. l c a lca lca r t rt rt 的祖先:同查询,记 l c a lca lca r t rt rt 这条路径上 l c a lca lca 的儿子为 s o n son son,我们可以先将全局和加上 x x x,再将 s o n son son 的子树和减去 x x x,这样相当于 s o n son son 的子树都没加。
  3. 其他情况下直接将 l c a lca lca 的子树和加 x x x

但是还有一个坑,就是 l c a lca lca 也会根据 r t rt rt 的位置而改变!

  1. u u u v v v 都在 r t rt rt 的子树内: l c a ← LCA(u,v) ⁡ lca\gets\operatorname{LCA(u,v)} lcaLCA(u,v)
  2. 其中一个在 r t rt rt 的子树内: l c a ← r t lca\gets rt lcart
  3. u u u v v v 都不在 r t rt rt 的子树内: l c a lca lca LCA(u,rt) ⁡ \operatorname{LCA(u,rt)} LCA(u,rt) LCA(v,rt) ⁡ \operatorname{LCA(v,rt)} LCA(v,rt) 中深度较大的。

综上,可以发现,以上 3 3 3 种情况的 l c a lca lca 都满足是 LCA(u,v) ⁡ , LCA(u,rt) ⁡ \operatorname{LCA(u,v)},\operatorname{LCA(u,rt)} LCA(u,v),LCA(u,rt) LCA(v,rt) ⁡ \operatorname{LCA(v,rt)} LCA(v,rt) 中深度最大的那个。

Code \Large\text{Code} Code

#include <iostream>
#include <cstdio>
#define int long long
#define re register
using namespace std;

inline int read()
{
	re int x = 0, f = 0;
	re char c = getchar();
	while (c < '0' || c > '9')
	{
		f |= c == '-';
		c = getchar();
	}
	while (c >= '0' && c <= '9')
	{
		x = (x << 3) + (x << 1) + (c ^ 48);
		c = getchar();
	}
	return f ? -x : x;
}

inline void write(int x)
{
	if (x < 0)
	{
		putchar('-');
		x = -x;
	}
	if (x > 9)
	{
		write(x / 10);
	}
	putchar(x % 10 ^ '0');
}

inline void swap2(int &x, int &y)
{
	x ^= y ^= x ^= y;
}

inline int min2(int x, int y)
{
	return x < y ? x : y;
}
//--------------------------------------------
const int MAXN = 1e5 + 5;

int cnt, Time;
int a[MAXN], b[MAXN], head[MAXN], fa[MAXN][18], lg[MAXN], dep[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], top[MAXN];

struct edge
{
	int to, nxt;
}e[MAXN << 1];

void add(int u, int v)
{
	e[++cnt] = edge{v, head[u]};
	head[u] = cnt;
}

void dfs1(int u, int father)
{
	fa[u][0] = father;
	dep[u] = dep[father] + 1;
	siz[u] = 1;
	for (re int i = 1; i <= 17; i++)
	{
		fa[u][i] = fa[fa[u][i - 1]][i - 1];
	}
	for (re int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (v == father)
		{
			continue;
		}
		dfs1(v, u);
		siz[u] += siz[v];
		if (siz[v] > siz[son[u]])
		{
			son[u] = v;
		}
	}
}

void dfs2(int u, int topp)
{
	dfn[u] = ++Time;
	a[Time] = b[u];
	top[u] = topp;
	if (son[u])
	{
		dfs2(son[u], topp);
	}
	for (re int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (top[v])
		{
			continue;
		}
		dfs2(v, v);
	}
}

int lca(int x, int y)
{
	if (dep[x] < dep[y])
	{
		swap2(x, y);
	}
	while (dep[x] > dep[y])
	{
		x = fa[x][lg[dep[x] - dep[y]]];
	}
	if (x == y)
	{
		return x;
	}
	for (re int i = 17; i >= 0; i--)
	{
		if (fa[x][i] != fa[y][i])
		{
			x = fa[x][i];
			y = fa[y][i];
		}
	}
	return fa[x][0];
}

int getfa(int x, int k)
{
	for (re int i = lg[k]; i >= 0; i--)
	{
		if (k >= (1 << i))
		{
			x = fa[x][i];
			k -= (1 << i);
		}
	}
	return x;
}

#define lson pos << 1
#define rson pos << 1 | 1

struct tree
{
	int l, r, siz, val, tag;
}t[MAXN << 2];

void pushup(int pos)
{
	t[pos].val = t[lson].val + t[rson].val;
}

void cal(int pos, int val)
{
	t[pos].val += val * t[pos].siz;
	t[pos].tag += val;
}

void pushdown(int pos)
{
	if (t[pos].tag)
	{
		cal(lson, t[pos].tag);
		cal(rson, t[pos].tag);
		t[pos].tag = 0;
	}
}

void build(int pos, int l, int r)
{
	t[pos].l = l, t[pos].r = r, t[pos].siz = r - l + 1;
	if (l == r)
	{
		t[pos].val = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(lson, l, mid);
	build(rson, mid + 1, r);
	pushup(pos);
}

void update(int pos, int L, int R, int k)
{
	int l = t[pos].l, r = t[pos].r;
	if (L <= l && r <= R)
	{
		cal(pos, k);
		return;
	}
	pushdown(pos);
	int mid = (l + r) >> 1;
	if (L <= mid)
	{
		update(lson, L, R, k);
	}
	if (R > mid)
	{
		update(rson, L, R, k);
	}
	pushup(pos);
}

int query(int pos, int L, int R)
{
	int l = t[pos].l, r = t[pos].r;
	if (L <= l && r <= R)
	{
		return t[pos].val;
	}
	pushdown(pos);
	int mid = (l + r) >> 1, res = 0;
	if (L <= mid)
	{
		res = query(lson, L, R);
	}
	if (R > mid)
	{
		res += query(rson, L, R);
	}
	return res;
}

void update_subtree(int x, int k)
{
	update(1, dfn[x], dfn[x] + siz[x] - 1, k);
}

int query_subtree(int x)
{
	return query(1, dfn[x], dfn[x] + siz[x] - 1);
}

int getlca(int x, int y, int z) //3个lca中深度最大的
{
	int a = lca(x, y), b = lca(x, z), c = lca(y, z);
	if (dep[a] < dep[b])
	{
		a = b;
	}
	if (dep[a] < dep[c])
	{
		a = c;
	}
	return a;
}

signed main()
{
	int n = read(), q = read(), rt = 1;
	for (re int i = 2; i <= n; i++)
	{
		lg[i] = lg[i >> 1] + 1;
	}
	for (re int i = 1; i <= n; i++)
	{
		b[i] = read();
	}
	for (re int i = 1; i < n; i++)
	{
		int u = read(), v = read();
		add(u, v);
		add(v, u);
	}
	dfs1(1, 0);
	dfs2(1, 1);
	build(1, 1, n);
	while (q--)
	{
		int op = read(), u = read(), v, x, p, q;
		switch (op)
		{
			case 1:
				rt = u;
				break;
			case 2:
				v = read(), x = read();
				u = getlca(u, v, rt);
				if (u == rt)
				{
					update(1, 1, n, x);
				}
				else if (lca(u, rt) == u)
				{
					v = getfa(rt, dep[rt] - dep[u] - 1);
					update(1, 1, n, x);
					update_subtree(v, -x); //将v的子树减掉
				}
				else
				{
					update_subtree(u, x);
				}
				break;
			case 3:
				if (u == rt)
				{
					write(query(1, 1, n));
				}
				else if (lca(rt, u) == u)
				{
					v = getfa(rt, dep[rt] - dep[u] - 1);
					write(query(1, 1, n) - query_subtree(v));
				}
				else
				{
					write(query_subtree(u));
				}
				putchar('\n');
				break;
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Three.js中的剖切功能可以通过设置剪裁平面对象来实现。通过WebGL渲染器的.clippingPlanes属性设置的剪裁平面对象Plane可以剪裁场景中的所有模型对象。如果你只想剪裁特定的模型对象,可以通过Three.js材质对象的.clippingPlanes属性来实现。要注意的是,一个网格模型所绑定的材质对象的.clippingPlanes属性如果没有设置,那么该模型对象就不会被剪裁。你可以据需要设置不同模型对象的.clippingPlanes属性来实现剖切效果。\[3\]在实际操作中,你可以导入的obj模型替代原本的网格模型,从而实现剖切及剖切面的绘制。不过这样并没有真正理解模型剖切的原理,如果你想深入学习模型剖切的原理,可以在研究场景渲染效果时加深对着色器的认识,并回过头来分析剖切的原理。\[2\] #### 引用[.reference_title] - *1* *2* [Three.js OBJ模型的剖切](https://blog.csdn.net/weixin_45699870/article/details/108879445)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Three.js剪裁模型](https://blog.csdn.net/u014291990/article/details/102812763)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值