Link

题意:对一棵树进行三种操作。

  • 把根设为 \(x\)。
  • 将以 \(lca(y, z)\) 为根的子树中所有点的权值加 \(v\)。
  • 查询以 \(x\) 为根的子树点权之和。

初始根为 \(1\)。

设相对于当前 \(root\) 的所要操作(查询)子树的根节点为 \(U\)。

这里用 \([in[x], out[x]]\) 表示以 \(x\) 为根的子树在 \(dfs\) 序上的出现区间。

情况一:\(U = root\)。

修改整棵树。

情况二:直接在原树上修改子树 \(U\)。

满足以下条件之一:

  • 整棵 \(U\) 在到 \(root\) 前已经遍历完了,\(out[U] < in[root]\)。
  • 整棵 \(root\) 在到 \(U\) 前已经遍历完了,\(in[U] > out[root]\)。
  • \(U\) 是 \(root\) 的后代,\(in[root] < in[U] < out[U] < out[root]]\)。

二三可以合写为 \(in[U] > in[root]\)。

情况三:\(root\) 为 \(U\) 的后代。

先整树修改,在扣除 \(U\) 在 \(root\) 方向的子树的影响。

如何求这个方向子树的根,可以借鉴 \(lca\) 的思想。

int get(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
per(i, 17, 0) if(dep[fa[x][i]] > dep[y]) x = fa[x][i];
return x;
}

操作三的 \(U\) 就是 \(x\)。

对于操作二,\(U\) 为 \(lca(y, z)\),\(lca(root, y)\),\(lca(root, z\) 中深度最大的点,可以自己模拟一下。

int lca(int a, int b, int c) {
int x = lca(a, b), y = lca(a, c);
if(dep[x] < dep[y]) x = y;
y = lca(b, c);
if(dep[x] < dep[y]) x = y;
return x;
}

具体做法就是线段树维护 \(dfs\) 序 + 大分讨,细节较多。

完整代码

#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); ++ i)
#define per(i, a, b) for(int i = (a); i >= (b); -- i)
#define pb emplace_back
#define All(X) X.begin(), X.end()
using namespace std;
using ll = long long;
constexpr int N = 1e5 + 5; int n, m, dep[N], in[N], out[N], dfn;
int fa[N][18];
ll a[N];
vector<int> G[N]; struct Node {
ll sum, tag;
int sz;
} t[N << 2]; #define ls x << 1
#define rs ls | 1 void build(int x = 1, int l = 1, int r = n) {
t[x].sz = r - l + 1;
if(l == r) return;
int mid = l + r >> 1;
build(ls, l, mid), build(rs, mid + 1, r);
} void pushup(int x) {
t[x].sum = t[ls].sum + t[rs].sum;
} void pushdown(int x) {
if(t[x].tag) {
t[ls].tag += t[x].tag, t[ls].sum += t[x].tag * t[ls].sz;
t[rs].tag += t[x].tag, t[rs].sum += t[x].tag * t[rs].sz;
t[x].tag = 0;
}
} void add(int L, int R, ll v, int x = 1, int l = 1, int r = n) {
if(L > R) return;
if(L <= l && r <= R) {
t[x].sum += v * t[x].sz;
t[x].tag += v;
return;
}
pushdown(x);
int mid = l + r >> 1;
if(mid >= L) add(L, R, v, ls, l, mid);
if(mid < R) add(L, R, v, rs, mid + 1, r);
pushup(x);
} ll query(int L, int R, int x = 1, int l = 1, int r = n) {
if(L > R) return 0;
if(L <= l && r <= R) {
return t[x].sum;
}
pushdown(x);
int mid = l + r >> 1;
ll ret = 0;
if(mid >= L) ret += query(L, R, ls, l, mid);
if(mid < R) ret += query(L, R, rs, mid + 1, r);
return ret;
} void dfs(int x) {
dep[x] = dep[fa[x][0]] + 1;
in[x] = ++ dfn;
add(dfn, dfn, a[x]); for(auto y : G[x]) {
if(y != fa[x][0]) {
fa[y][0] = x;
rep(i, 1, 17) fa[y][i] = fa[fa[y][i - 1]][i - 1];
dfs(y);
}
}
out[x] = dfn;
} int lca(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
per(i, 17, 0) if(dep[fa[x][i]] >= dep[y] ) x = fa[x][i];
if(x == y) return x;
per(i, 17, 0) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
int lca(int a, int b, int c) {
int x = lca(a, b), y = lca(a, c);
if(dep[x] < dep[y]) x = y;
y = lca(b, c);
if(dep[x] < dep[y]) x = y;
return x;
}
int get(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
per(i, 17, 0) if(dep[fa[x][i]] > dep[y]) x = fa[x][i];
return x;
} int root = 1; int main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
cin >> n >> m;
rep(i, 1, n) cin >> a[i];
rep(i, 2, n) {
int x, y; cin >> x >> y;
G[x].pb(y);
G[y].pb(x);
}
build(), dfs(1);
rep(i, 1, m) {
int op; cin >> op;
if(op == 1) {
cin >> root;
}
if(op == 2) {
int y, z, v; cin >> y >> z >> v;
int x = lca(root, y, z); if(x == root) {
add(1, n, v);
}
else {
if(in[x] > in[root] || out[x] < in[root]) {
add(in[x], out[x], v);
}
else {
int Fa = get(x, root);
add(1, n, v);
add(in[Fa], out[Fa], -v);
}
}
}
if(op == 3) {
int x; cin >> x;
if(x == root) {
cout << t[1].sum << '\n';
}
else {
if(in[x] > in[root] || out[x] < in[root]) {
cout << query(in[x], out[x]) << '\n';
}
else {
int Fa = get(x, root);
cout << t[1].sum - query(in[Fa], out[Fa]) << '\n';
}
}
}
}
return 0;
}

CF916E 换根树上问题的更多相关文章

  1. 2019ICPC沈阳网络赛-D-Fish eating fruit(树上DP, 换根, 点分治)

    链接: https://nanti.jisuanke.com/t/41403 题意: State Z is a underwater kingdom of the Atlantic Ocean. Th ...

  2. 树链剖分(附带LCA和换根)——基于dfs序的树上优化

    .... 有点懒: 需要先理解几个概念: 1. LCA 2. 线段树(熟练,要不代码能调一天) 3. 图论的基本知识(dfs序的性质) 这大概就好了: 定义: 1.重儿子:一个点所连点树size最大的 ...

  3. POJ - 3585 树上最大流 换根法

    题意:给出一棵树,边上有容量限制,求以任一点作为根和源点,叶子作为汇点的最大流的最大值 首先上网络流等于找死 树形DP可以\(O(n)\)求出以某点\(u\)为根的最大流,只需设\(f[u]=\sum ...

  4. Acwing-287-积蓄程度(树上DP, 换根)

    链接: https://www.acwing.com/problem/content/289/ 题意: 有一个树形的水系,由 N-1 条河道和 N 个交叉点组成. 我们可以把交叉点看作树中的节点,编号 ...

  5. [模板] dfs序, 树链剖分, 换根

    树链剖分 树链剖分是一种对树的分治, 可以把树上的任意一条链分解为 \(O(\log n)\) 条在dfs序上相邻的子链, 便于数据结构(如线段树)来维护. 另外, 子树在dfs序上也是一个连续的区间 ...

  6. 【树链剖分换根】P3979 遥远的国度

    Description zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcw ...

  7. POJ3585:Accumulation Degree(换根树形dp)

    Accumulation Degree Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 3425   Accepted: 85 ...

  8. 题解 poj3585 Accumulation Degree (树形dp)(二次扫描和换根法)

    写一篇题解,以纪念调了一个小时的经历(就是因为边的数组没有乘2 phhhh QAQ) 题目 题目大意:找一个点使得从这个点出发作为源点,流出的流量最大,输出这个最大的流量. 以这道题来介绍二次扫描和换 ...

  9. [题解](树形dp/换根)小x游世界树

    2. 小x游世界树 (yggdrasi.pas/c/cpp) [问题描述] 小x得到了一个(不可靠的)小道消息,传说中的神岛阿瓦隆在格陵兰海的某处,据说那里埋藏着亚瑟王的宝藏,这引起了小x的好奇,但当 ...

  10. poj 3585 Accumulation Degree(二次扫描和换根法)

    Accumulation Degree 大致题意:有一棵流量树,它的每一条边都有一个正流量,树上所有度数为一的节点都是出口,相应的树上每一个节点都有一个权值,它表示从这个节点向其他出口可以输送的最大总 ...

随机推荐

  1. objective-c之Class底层结构探索

    isa 走位图 在讲 OC->Class 底层类结构之前,先看下下面这张图: 通过isa走位图 得出的结论是: 1,类,父类,元类都包含了 isa, superclass 2,对象isa指向类对 ...

  2. KingbaseES 实现 MySQL 函数 last_insert_id

    用户从mysql迁移到金仓数据库过程中,应用中使用了mysql函数last_insert_id()来获取最近insert的那行记录的自增字段值. mysql文档中关于函数的说明和例子: LAST_IN ...

  3. 开源鸿蒙(OpenHarmonyOS)代码下载及编译

    开源鸿蒙的代码仓在码云上,可以通过以下命令下载源码并编译 本机安装虚拟机 如本地已经安装可以忽略此步 安装指导:https://thoughts.teambition.com/share/614c49 ...

  4. archlinux 安装后xfce没有声音,声音无法调节

    参照 http://ivo-wang.github.io/2018/02/17/fix/ sudo pacman -S alsa-utils pavucontrol sudo pacman -S pi ...

  5. 卷积神经网络学习笔记——ZFNet(Tensorflow实现)

    完整代码及其数据,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/DeepLearningNote 这个网络应该是CNN的鼻 ...

  6. Linux内核数据管理利器--红黑树

    目录 写在前面 1. 红黑树的原理 2. 红黑树操作 2.1 红黑树的节点插入 2.2 红黑树的节点删除 2.3 红黑树的查询操作 3. 红黑树操作实验 附录A: 实验代码 写在前面 本文通过两个方面 ...

  7. .NET Emit 入门教程:第六部分:IL 指令:4:详解 ILGenerator 指令方法:参数存储指令

    前言: 上一篇介绍了 IL 指令的分类以及参数加载指令,该加载指令以ld开头,将参数加载到栈中,以便于后续执行操作命令. 本篇开始介绍参数存储指令,其指令以st开头,将栈中的数据,存储到指定的变量中, ...

  8. 李东山:如何让 OpenHarmony 支持低功耗蓝牙芯片 GR551x

    编者按:在 OpenHarmony 生态发展过程中,涌现了大批优秀的代码贡献者,本专题旨在表彰贡献.分享经验,文中内容来自嘉宾访谈,不代表 OpenHarmony 工作委员会观点. 李东山 深圳市汇顶 ...

  9. 基于eTS高效开发HarmonyOS课程类应用

    原文:https://mp.weixin.qq.com/s/kU76kB6T1JSqapAfGPGRHQ,点击链接查看更多技术内容. 随着HarmonyOS 3.0 Beta版的发布,API Vers ...

  10. 第十四篇:JavaScript基础

    一.CSS内容补充之position 10.position:fixed:固定div在页面的一个位置: top:0; right:0; left:0; position:absolute + rela ...