CF916E 换根树上问题
题意:对一棵树进行三种操作。
- 把根设为
。 - 将以
为根的子树中所有点的权值加 。 - 查询以
为根的子树点权之和。
初始根为
设相对于当前
这里用
表示以 为根的子树在 序上的出现区间。
情况一: 。#
修改整棵树。
情况二:直接在原树上修改子树 。#
满足以下条件之一:
- 整棵
在到 前已经遍历完了, 。 - 整棵
在到 前已经遍历完了, 。 是 的后代, 。
二三可以合写为
。
情况三: 为 的后代。#
先整树修改,在扣除
如何求这个方向子树的根,可以借鉴
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 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;
}
具体做法就是线段树维护
完整代码
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】