当前位置: 首页 > news >正文

「Gym 102759I」Query On A Tree 17

题目大意

给定一颗 \(N\) 个节点以 \(1\) 为根的有根树,每次给以 \(u\) 为根的子树每点加 \(1\) 的值或给路径 \(u - v\) 上每点加 \(1\) 的值,每次修改后查询一个点 \(u\) 使得 \(\sum_{v = 1}^N dis(u, v)\) 最小。

题目转换

首先我们很贪心地想到点 \(u\) 是树的带权重心,证明如下:

假设点 \(u\) 是带权重心,\(sum_u\)\(u\) 的子树权值和,另取一点 \(v (v \neq u)\),令 \(u\) 为根,则对在路径 \(u - v\) 上每一点 \(p (p \neq u)\)

\[\sum_{i = 1}^N dis(i, p) - \sum_{i = 1}^N dis(i, fa_p) = N - 2sum_p \geq 0 \]

所以从 \(u\)\(v\) 的移动过程中每一步都不优,故 \(u\) 为我们的答案。

所以我们每次加子树,加路径,查带权重心。

然后再原树上带权重心 \(u\) 的子树权值和一定刚好严格大于整棵树权值和一半,否则一定不优,证明同上。换做 dfs 序,即一个区间权值和大于数列和一半。

做法详解

现在我们明确一下我们要求什么:找到一个深度最深的点 \(u\) 使得在 dfs 序下 \([dfn_u, dfn_u + siz_u - 1]\) 的权值和严格大于 \([1, n]\) 的权值和。

考虑怎么去找,我们先要猜个结论,设 \(pre_i\) 表示在 dfs 序下的权值前缀和,再找到一个 \(j\) 使得 \(pre_{j - 1}, pre_n - pre_j \leq \left \lfloor pre_n \right \rfloor\),则我们找到的点 \(u\) 一定满足 \(j \in [dfn_u, dfn_u + siz_u - 1]\) ,为什么呢?很简单,我们的 \([dfn_u, dfn_u + siz_u - 1]\) 权值和大于总权值一半,而无论是 \(pre_{j - 1}\) 还是 \(pre_n - pre_j\)\(j\) 的左右两边都小于总权值一半,所以我们的区间一定不会被包含于 \([1, j - 1]\)\([j + 1, n]\) ,故 \(rnk_j\) (dfs 序为 \(j\) 的结点) 一定在 \(u\) 的子树中。

\(rnk_j\) 很好找,然后我们因为要找深度最深的那个满足条件的点,且子树和满足单调性,所以满足条件的点一定是在树上连续的,故考虑倍增,每次check跳上去的点是否满足要求,若是,则跳,反之不跳,最后再跳 \(1\) 步(如果 \(rnk_j\) 满足条件就不管)得到答案 \(u\)

Solution

挺好实现的,思路清晰,大多都是模板,就是代码微长(也就百来行)。

/*
address:https://vjudge.net/problem/Gym-102759I
AC 2025/10/28 20:29
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
int dfn[N], siz[N], top[N], son[N], fa[N], dep[N], rnk[N];
int cntn;
vector<int>G[N];
int n, q;
struct SegmentTree {
#define ls (id << 1)
#define rs (id << 1 | 1)
#define mid (l + r >> 1)LL sum[N << 2], add[N << 2];inline void pushup(int id) { sum[id] = sum[ls] + sum[rs]; }inline void pushdown(int id, int l, int r) {if (add[id]) {add[ls] += add[id];add[rs] += add[id];sum[ls] += add[id] * (mid - l + 1);sum[rs] += add[id] * (r - mid);add[id] = 0;}}inline void modify(int id, int l, int r, int L, int R) {if (l >= L && R >= r) {sum[id] += r - l + 1;++add[id];return;}pushdown(id, l, r);if (L <= mid) modify(ls, l, mid, L, R);if (R > mid) modify(rs, mid + 1, r, L, R);pushup(id);}inline LL query(int id, int l, int r, int L, int R) {if (l >= L && R >= r) return sum[id];pushdown(id, l, r);LL ret = 0;if (L <= mid) ret += query(ls, l, mid, L, R);if (R > mid) ret += query(rs, mid + 1, r, L, R);return ret;}inline int search(int id, int l, int r, LL k) {if (l == r) return sum[id] <= k ? l : l - 1;pushdown(id, l, r);if (sum[ls] <= k) return search(rs, mid + 1, r, k - sum[ls]);else return search(ls, l, mid, k);}
}SGT;
const int K = 20;
int up[K][N];
inline void dfs1(int u) {dep[u] = dep[fa[u]] + 1;up[0][u] = fa[u];for (int i = 1;i < K;++i) up[i][u] = up[i - 1][up[i - 1][u]];siz[u] = 1;son[u] = 0;for (auto v : G[u])if (v != fa[u]) {fa[v] = u;dfs1(v);siz[u] += siz[v];if (siz[v] > siz[son[u]]) son[u] = v;}
}
inline void dfs2(int u) {dfn[u] = ++cntn;rnk[cntn] = u;if (!son[u]) return;top[son[u]] = top[u];dfs2(son[u]);for (auto v : G[u])if (v != son[u] && v != fa[u]) {top[v] = v;dfs2(v);}
}
LL sum;
inline void update(int u, int v) {while (top[u] != top[v]) {if (dep[top[u]] < dep[top[v]]) swap(u, v);SGT.modify(1, 1, n, dfn[top[u]], dfn[u]);sum += dfn[u] - dfn[top[u]] + 1;u = fa[top[u]];}if (dep[u] > dep[v]) swap(u, v);SGT.modify(1, 1, n, dfn[u], dfn[v]);sum += dfn[v] - dfn[u] + 1;
}
int main() {scanf("%d", &n);for (int i = 1;i < n;++i) {int u, v;scanf("%d%d", &u, &v);G[u].push_back(v);G[v].push_back(u);}dfs1(1);dfs2(1);scanf("%d", &q);while (q--) {int op, u;scanf("%d%d", &op, &u);if (op == 1) SGT.modify(1, 1, n, dfn[u], dfn[u] + siz[u] - 1), sum += siz[u];if (op == 2) {int v;scanf("%d", &v);update(u, v);}int p = SGT.search(1, 1, n, sum >> 1) + 1;u = rnk[p];for (int i = K - 1;i >= 0;--i)if (up[i][u] && SGT.query(1, 1, n, dfn[up[i][u]], dfn[up[i][u]] + siz[up[i][u]] - 1) <= sum >> 1) u = up[i][u];if (up[0][u] && SGT.query(1, 1, n, dfn[u], dfn[u] + siz[u] - 1) <= sum >> 1) u = up[0][u];printf("%d\n", u);}return 0;
}
http://icebutterfly214.com/news/1503/

相关文章:

  • 巧用 using 作用域(IDisposable)的生命周期包装特性 实现前后置处理
  • CSP-S 41多校 9
  • [题解]P5322 [BJOI2019] 排兵布阵
  • MySQL 查询与更新语句执行过程深度解析:从原理到实践​ - 指南
  • 逆向基础--编码(001)
  • Luogu P7913 [CSP-S 2021] 廊桥分配 题解 [ 绿 ] [ 贪心 ] [ 前缀和 ] [ STL ]
  • 完整教程:Docker 搭建 Nginx 并启用 HTTPS 具体部署流程
  • 10.28代码大全2
  • [GESP202509 二级] 菱形
  • linux 配置vnc
  • 第七周第二天7.2
  • 完整教程:IP 地址管理:IPv4 和 IPv6 地址规划、子网划分与 CIDR
  • Day25-C:\Users\Lenovo\Desktop\note\code\JavaSE\Basic\src\com\Threadcase-多线程讲到等待唤醒机制的一半
  • C++primer 类的静态成员
  • 【硬件测试】基于FPGA的8PSK+帧同步系统开发与硬件片内测试,包含高斯信道,误码统计,可设置SNR
  • 习题-良序集
  • P6147 [USACO20FEB] Delegation G 题解
  • 收藏版:Phinx 数据库迁移完全指南
  • ts相关
  • 【每日一面】async/await 的原理
  • 0288-KVS-根据索引读取文件
  • 2025精密/五金/冲压/塑料/模具配件/司筒/镶件/零件企业推荐榜:锦鸿深耕三十载筑服务网络,这些实力派值得关注​
  • 2025弯管领域源头厂家推荐榜:合肥市翼达机械领衔,多企业助力工业管件加工升级​
  • 2025年碳氢肥料生产厂家权威推荐榜单:农产品用料/增产用肥/碳氢核肥邮沃源头厂家精选
  • my.conf脚本备份
  • SQL改写:99%DBA估计都会忽略的重大知识点
  • 蓝狐家庭维修小程序系统:一站式家庭维修服务解决方案
  • 达梦删除数据文件后恢复
  • 《植物大战僵尸:重植版》无障碍补丁 | An accessibility mod for Plants vs. Zombies™: Replanted
  • 【System Beats!】第五章 优化程序性能