洛谷P3979 遥远的国度 树剖+线段树+换根


洛谷P3979 遥远的国度


标签

  • 树链剖分
  • LCA
  • 换根

简明题意

  • 给一棵有根树,支持3种操作:
    1. 修改:把树的根改为id
    2. 修改:把u-v路径上的权值改为v
    3. 查询:查询当前根下,x的子树中的最小权值

思路

  • 这里比较麻烦的是换根操作。具体怎么处理呢?当然是不可能每次换根都重新dfs一次。看下图,假设1是最开始的跟,我们从1节点dfs过两次。现在假设root换成了4(1号节点仍然是原根)
    在这里插入图片描述
  • 换根后成了这样:
    在这里插入图片描述
  • 我们可以发现,有很多节点的子树并没有改变,下图用紫色矩形圈出了没有发生变化的节点,红色矩形圈出的是子树发生了变化的节点:
    在这里插入图片描述
  • 也就是说,只有从新根到原根的那一条链(红色矩形圈出)、以及新的根(4号节点)的子树发生了变化,其他节点的子树都没有发生改变。为什么会这样呢?我们可以从父节点的角度考虑,大家模拟一遍以4为新根把树“提起来”的过程,可以发现,只有新根到原根连成的那一条链上的节点的父节点改变了(包括新根和原根)。也就是说除了这条链上的节点外,其他的节点仍按照原先的做法去查询就可以了。现在问题来了,如何对这条链上的节点进行查询操作?
  • 设查询的点为x,整棵树-x到新根的路径中x的直系儿子,就是答案了。这里要查的节点是x,x的直系儿子即为u,那么u的范围就是 id[u]---------id[u] + siz[u] - 1,所以呢,我们要查的范围就是1----id[u]-1和id[u]+siz[u]------n。这里会出现一个很严重的问题,就是当我们找到的u的子树中包含了n号节点,那么我们查找的就不是两个范围了,而是一个范围,1----id[u],因为回忆我们查的范围id[u]+siz[u]-----n,其中id[u]+siz[n] = n + 1,仍然查两个范围会RE(这里感谢洛谷用户@chdy帮我查出的bug~~~如果不是她,我估计会多挣扎好多个小时)
  • 好了,最后一个问题就是,怎么找到x的直系儿子。这里直系儿子指:x的 在x到新跟路径上的儿子。我在这里直接遍历x的所有儿子v,如果LCA(v, 新根) == v,就说明v是x的直系儿子。

注意事项

  • 就是一棵子树,如果子树中包含了n号节点,那么这棵子树的右边是没有区间的!不能查它右边!否则RE!!!

总结

  • 换根的题,找对关系分类讨论就好了

AC代码

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;

const int maxn = 100000 + 10;

int n, m, a[maxn];
int root, cur_root;
vector<int> g[maxn];

int dep[maxn], fa[maxn], siz[maxn], son[maxn];
void dfs1(int u, int f, int deep)
{
   
   dep[u] = deep;
   fa[u] = f;
   siz[u] = 1;

   int max_son = -1;
   for (auto& v : g[u])
   	if (v != f)
   	{
   
   		dfs1(v, u, deep + 1);
   		siz[u] += siz[v];
   		if (siz[v] > max_son)
   			max_son = siz[v], son[u] = v;
   	}
}

int top[maxn], id[maxn], cnt, w[maxn];
void dfs2(int u, int topf)
{
   
   id[u] = ++cnt;
   w[cnt] = a[u];
   top[u] = topf;

   if (son[u])
   {
   
   	dfs2(son
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值