树链剖分换根

一、题目

描述
给定一棵 n 个节点的树,初始时该树的根为 1 号节点,每个节点有一个给定的权值。下面依次进行 m 个操作,操作分为如下五种类型:
换根:将一个指定的节点设置为树的新根。
修改路径权值:给定两个节点,将这两个节点间路径上的所有节点权值(含这两个节点)增加一个给定的值。
修改子树权值:给定一个节点,将以该节点为根的子树内的所有节点权值增加一个给定的值。
询问路径:询问某条路径上节点的权值和。
询问子树:询问某个子树内节点的权值和。

输入
第一行为一个整数 n,表示节点的个数。
第二行 n 个整数表示第i个节点的初始权值 ai
第三行 n−1 个整数,表示i+1 号节点的父节点编号 fi+1 (1⩽fi+1⩽n)
第四行一个整数 m,表示操作个数。
接下来 m 行,每行第一个整数表示操作类型编号:(1⩽u,v⩽n)
若类型为 1,则接下来一个整数 u,表示新根的编号。
若类型为 2,则接下来三个整数 u,v,k,分别表示路径两端的节点编号以及增加的权值。
若类型为3,则接下来两个整数 u,k,分别表示子树根节点编号以及增加的权值。
若类型为 4,则接下来两个整数u,v,表示路径两端的节点编号。
若类型为 5,则接下来一个整数 u,表示子树根节点编号。

输出
对于每一个类型为 4 或 5 的操作,输出一行一个整数表示答案。

二、解法

这道题总体很板,不过换根的操作很清奇,我们来详细讨论一下。
可以发现换根对路径查询&修改没有影响。
我们考虑不改变树的形状,分类讨论换根对子树查询&修改的影响:
1、如果 u = r o o t u=root u=root,则直接操作整棵树。
2、如果 l c a ( u , r o o t ) ≠ u lca(u,root)\not=u lca(u,root)=u,即根不在 u u u的子树内,则根对子树的操作无影响。
3、如果 l c a ( u , r o o t ) = u lca(u,root)=u lca(u,root)=u,即根在 u u u的子树内,我们定义 v v v r o o t root root u u u的树上路径上的最后一个点,则 v v v的子树是对于全集的操作集的补集。
分上面三种情况讨论,这道题就可以当板子来做了。

#include <cstdio>
#include <iostream>
#define int long long
using namespace std;
const int MAXN = 100005;
int read()
{
   
    int x=0,flag=1;
    char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int n,m,tot,root=1,a[MAXN],f[MAXN],fa
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值