尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

题解:Luogu P9260 [PA 2022] Miny

题解:Luogu P9260 [PA 2022] Miny
📅 发布时间:2026/6/19 0:22:57

题意

给定一棵 \(n\) 个点的树,第 \(i\) 条边 \((a_i,b_i)\) 有边权 \(c_i\),第 \(i\) 个点有一个爆炸半径 \(r_i\)。当一个点被引爆时,所有在该点爆炸半径范围内的点也会被引爆,这些新的被引爆的点也可能继续引爆其他点……对于每个点 \(i\),求出该点被引爆后,最终会被引爆的点的数量。\(1\leq n\leq 10^5\),\(0\leq r_i\leq 10^{18}\),\(1\leq c_i\leq 10^{12}\)。

题解

rerererecallllll。

比较毒瘤。

考虑建图,对于每个点 \(i\),向距离它不超过 \(r_i\) 的点 \(j\) 连一条有向边。对这个有向图缩点,考察一个强连通分量,显然引爆其中任何一个点都会让整个强连通分量中的点被引爆,于是每个点的答案就是在这个缩点后的 DAG 中可达的点数。

这样建图边数会爆炸,考虑优化建图。注意到建图条件是距离相关,因此考虑点分治优化建图。具体来说,设当前分治中心为 \(rt\),考察所有 \(rt\) 子树内的点。设 \(dis_u\) 为 \(u\) 到 \(rt\) 的距离,那么对于处于 \(rt\) 不同的儿子子树内的两点 \(u,v\),\(u\) 向 \(v\) 连边的条件为 \(dis_u+dis_v\leq r_u\)。而对于相同儿子子树内的两点,我们同样沿用这个条件即可,因为这相当于把限制放宽松了,并且我们向下递归到两点的 LCA 时,也会正确处理这两个点间的连边。

将 \(rt\) 子树内所有点按 \(dis\) 排序,枚举点 \(u\),那么它连出去的点 \(v\) 需要满足 \(dis_v\leq r_u-dis_u\),是一段前缀的点。我们可以使用前缀优化建图,建出 \(|sub_{rt}|\) 个虚点,从左到右第 \(i\) 个虚点,向第 \(i-1\) 个虚点连边,同时也要向排序后第 \(i\) 小的点连边。每次枚举点 \(u\) 时我们二分出分界点,向该分界点对应的虚点即可。这样我们就把图的点数和边数都压到了 \(\mathcal{O}(n\log{n})\) 级别,点分治建图的复杂度则是 \(\mathcal{O}(n\log^2{n})\) 的。

把图建出来缩点后,剩下的部分就是 DAG 可达性了。我们当然考虑建反图,在拓扑排序的过程中用 bitset 维护,但是这样子对于每个强连通分量我们都要开一个大小为 \(n\) 的 bitset,时空复杂度都是 \(\mathcal{O}\left(\dfrac{n^2\log{n}}{\omega}\right)\) 的,空间上无法承受。我们不妨分块,设块长为 \(B\),每次取一个长度为 \(\mathcal{O}(B)\) 的编号区间 \([l,r]\) 出来,跑 DAG 可达性时只考虑这里面的点。这样空间复杂度降至 \(\mathcal{O}\left(\dfrac{nB\log{n}}{\omega}\right)\),时间复杂度理论不变。

总体时间复杂度为 \(\mathcal{O}\left(n\log^2{n}+\dfrac{n^2\log{n}}{\omega}\right)\)。

这就能通过本题了吗?并不能。本题非常毒瘤,一些未经优化的地方常数是比较大的。接下来讲讲我是如何卡常的。

取不同的 \(B\),时间复杂度只是理论不变,但实际运行中取稍大的 \(B\) 会对内存访问更友好,以我的实现取 \(B=1500\) 较为优秀。

可以发现我们不必显式跑拓扑排序。我们知道 Tarjan 跑出来的强连通分量编号是逆拓扑序,于是可以把 DAG 缩点后的反图中的每条边 \((u,v)\) 以 \(u\) 为第一关键字、\(v\) 为第二关键字排序,跑 DAG 可达性时只需依次访问每条边 \((u,v)\),令 \(f_v\leftarrow f_v\cup f_u\) 即可。这样子没有了建图和遍历出边的开销,常数也优化了不少。

显然只有 \(1\sim n\) 所在的强连通分量是有用的,所以枚举强连通分量时的个数上界,应该设置成 \(1\sim n\) 所在的强连通分量的最大编号。

最后是最显著的优化。前缀优化建图时,\(\mathcal{O}(n\log{n})\) 个虚点带来的开销是巨大的,我们不妨只保留有用的那些。具体来说,对于一轮分治,我们把枚举点 \(u\) 时二分出的那些分界点对应的虚点视作关键点,对于相邻的关键点 \(p_i,p_{i+1}\),把 \([p_i+1,p_{i+1}]\) 中的点全部缩成 \(p_{i+1}\) 这一个点。虽然这样子图的点数和边数的理论量级不变,但是实际数量会显著减少。

加上上面这些优化就跑的飞快了。

代码

这里放出主要的部分。

inline void dfs_rt(int x, int fx, int szrt) {int mxp = 0; sz[x] = 1;for (int i = T.head[x]; i; i = T.nxt[i]) {int y = T.to[i];if (!del[y] && y != fx) dfs_rt(y, x, szrt), sz[x] += sz[y], chk_max(mxp, sz[y]);}chk_max(mxp, szrt - sz[x]);if (mxp << 1 <= szrt) rt = x;
}
inline void dfs_dis(int x, int fx) {sz[x] = 1, pt[++szp] = {dis[x], x};for (int i = T.head[x]; i; i = T.nxt[i]) {int y = T.to[i]; ll z = T.w[i];if (!del[y] && y != fx) dis[y] = dis[x] + z, dfs_dis(y, x), sz[x] += sz[y];}
}
inline void dfs_solve(int x, int szrt) {if (szrt == 1) return;rt = 0, dfs_rt(x, 0, szrt), del[rt] = 1;szp = dis[rt] = 0, dfs_dis(rt, 0);sort(pt + 1, pt + szp + 1), fill(num + 1, num + szp + 1, 0);for (int i = 1; i <= szp; ++i) {int px = pt[i].second; ll dx = dis[px];int p = upper_bound(pt + 1, pt + szp + 1, pli{d[px] - dx, n + 1}) - pt - 1;if (p) {if (!num[p]) num[p] = ++tot;G.ins(px, num[p]);}}int lst = 0;for (int i = szp; ~i; --i) {if (i && !num[i]) continue;if (lst) {if (i) G.ins(num[lst], num[i]);for (int j = i + 1; j <= lst; ++j) G.ins(num[lst], pt[j].second);}lst = i;}for (int i = T.head[rt]; i; i = T.nxt[i]) {int y = T.to[i];if (!del[y]) dfs_solve(y, sz[y]);}
}
inline void tarjan(int x) {low[x] = dfn[x] = ++stmp, in_stk[stk[++top] = x] = 1;for (int i = G.head[x]; i; i = G.nxt[i]) {int y = G.to[i];if (!dfn[y]) tarjan(y), chk_min(low[x], low[y]);else if (in_stk[y]) chk_min(low[x], dfn[y]);}if (low[x] == dfn[x]) {++cscc; int p;do p = stk[top--], id[p] = cscc, in_stk[p] = 0; while (p != x);}
}
inline void solve(int l, int r) {for (int i = 1; i <= mxt; ++i) f[i].reset();for (int i = l; i <= r; ++i) f[id[i]][i - l] = 1;for (int i = 1; i <= sze; ++i) f[edges[i].second] |= f[edges[i].first];for (int i = 1; i <= n; ++i) ans[i] += f[id[i]].count();
}int main() {ios::sync_with_stdio(0), cin.tie(0);n = rd();for (int i = 1; i <= n; ++i) d[i] = rd();ll w; for (int i = 1, u, v; i < n; ++i) u = rd(), v = rd(), w = rd(), T.ins(u, v, w), T.ins(v, u, w);dfs_solve(1, tot = n);for (int i = 1; i <= tot; ++i) if (!dfn[i]) tarjan(i);for (int i = 1; i <= n; ++i) chk_max(mxt, id[i]);for (int x = 1; x <= tot; ++x) for (int i = G.head[x]; i; i = G.nxt[i]) {int y = G.to[i];if (id[x] != id[y]) edges[++sze] = {id[y], id[x]};}sort(edges + 1, edges + sze + 1);for (int i = 1; i <= n; i += B) solve(i, min(i + B - 1, n));for (int i = 1; i <= n; ++i) cout << ans[i] << ' ';return 0;
}

相关新闻

  • 题解:Luogu P13544 [OOI 2022] Serious Business
  • 题解:Luogu P14254 分割(divide)
  • 构造单

最新新闻

  • 昆明全品类贵金属回收指南,金价实时更新,线下靠谱门店汇总清单 - 奢侈品回收评测
  • 沪上贵金属变现干货汇总:2026 五大黄金回收连锁门店全维度评测 - 奢侈品回收测评
  • 从零开发Java面试刷题作战APP:架构重构、模块闭环、技术栈选型全方案
  • 洪湖上门回收黄金哪家放心 2026大盘行情与避坑全攻略 - 润富黄金回收
  • 曲靖哪里回收黄金靠谱 2026六月实测三家实体门店无套路 - 润富黄金回收
  • 2026苏州黄金回收门店梯队测评,个人闲置黄金变现优选与避雷完整指南 - 奢侈品交易观察员

日新闻

  • 5分钟掌握Python进化算法:Geatpy高性能优化工具完全指南
  • Microchip 24AA044 EEPROM选型与应用全指南:从参数解析到实战编程
  • 华为的鸿蒙到底有多牛?为什么称作遥遥领先?

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号