按位与最大生成树:Kruscal与并查集的变形应用
1. 从一道题看最小生成树的本质Clarke and MST 这个标题第一次看到的时候我以为是某位选手的ID加上一道模板题点进去才发现里面藏着一个挺有意思的变形。题目大意是给定一张图每条边有一个权值要求找出一棵生成树使得所有边权按位与AND之后的值最大。这个按位与最大的目标和传统的最小生成树边权和最小完全不是一个路子所以直接套Kruscal模板是过不了的得先想清楚这个目标函数的性质。我先把结论摆出来按位与运算有一个关键性质——高位优先。也就是说二进制下如果第k位能变成1那么哪怕后面所有低位全是0结果也比第k位是0、后面全是1要大。这个性质直接决定了贪心策略的可行性我们从高位到低位逐位尝试看能不能构造出一棵生成树使得当前这一位以及之前已经确定为1的高位在最终结果里保持为1。具体做法是从第30位或者根据权值范围取更高位开始往下枚举维护一个当前允许使用的边集。对于第i位我们只保留那些权值在第i位为1的边然后在这个边集上跑Kruscal看能不能形成一棵生成树。如果能说明这一位可以贡献到最终答案里答案加上这一位同时把边集更新为这个子集如果不能说明这一位无论如何都保不住答案这一位就是0边集不变继续看下一位。这个思路为什么对因为按位与的结果只关心哪些位最终是1。我们贪心地从高位往低位确定每次只在能形成生成树的前提下保留这一位为1的边这样高位一旦确定就不会被低位影响符合按位与高位优先的特性。而Kruscal在这里的作用就是判断当前边集能否构成生成树——只要能选出n-1条边且不形成环就说明可以。2. 并查集在其中的角色与实现细节并查集在这道题里承担的是连通性判定的职责。Kruscal的核心就是按边权排序后依次加边用并查集判断加入这条边会不会成环。但在这道题里我们其实不需要对边排序——因为我们的目标不是最小化边权和而是判断当前边集能否构成生成树。所以Kruscal的排序这一步可以省掉直接遍历边集能加就加最后看加入了多少条边。不过这里有个细节需要注意每次尝试某一位时并查集必须重新初始化。因为上一位的连通状态和这一位要用的边集不一定兼容不能复用。我见过有人为了省事在循环外面初始化一次并查集结果WA到怀疑人生。这个坑很典型因为并查集的parent数组是会被路径压缩修改的复用会导致连通性判断完全错乱。并查集的实现我用的是带路径压缩的版本没有加按秩合并因为这道题的数据规模通常不会太大n和m一般在1e5以内路径压缩已经足够把单次find的均摊复杂度压到接近常数。如果你追求极致性能可以加上按秩合并但实际测下来差别不大。int parent[100005]; int find(int x) { if (parent[x] ! x) parent[x] find(parent[x]); return parent[x]; } bool unite(int a, int b) { int ra find(a), rb find(b); if (ra rb) return false; parent[ra] rb; return true; }上面这段代码是并查集的核心。注意find里的路径压缩写法递归版本在数据量大时可能爆栈可以改成迭代版本。我在实际写的时候习惯用迭代虽然代码稍微长一点但心里踏实。3. 逐位贪心的完整实现与参数选择现在把整个算法的流程串起来。假设边权最大不超过2^30那么我们从第30位枚举到第0位。每一位的处理分三步第一步把当前边集中所有第i位为1的边筛选出来形成候选边集第二步用并查集在候选边集上跑Kruscal统计能加入的边数第三步如果加入的边数等于n-1说明这一位可以保留答案加上(1i)同时把当前边集更新为候选边集否则这一位放弃边集不变。这里有一个容易忽略的点边集的更新时机。只有在当前位能形成生成树时我们才把边集缩小到候选边集。如果这一位不能形成生成树我们继续用原来的边集去看下一位。这个逻辑保证了我们不会因为某一位保不住而丢失更高位已经确定的信息。关于枚举的起始位我一般取30因为大多数题目的边权在int范围内。如果边权可能达到long long范围那就取60。但实际比赛中30位已经足够覆盖绝大多数情况。如果你不确定可以先把所有边权的最大值求出来然后取它的最高位作为起始位这样能省掉一些无用的枚举。int n, m; struct Edge { int u, v, w; } edges[200005]; int solve() { int ans 0; vectorEdge cur(edges, edges m); for (int bit 30; bit 0; bit--) { vectorEdge nxt; for (auto e : cur) { if (e.w bit 1) nxt.push_back(e); } // 初始化并查集 for (int i 1; i n; i) parent[i] i; int cnt 0; for (auto e : nxt) { if (unite(e.u, e.v)) cnt; } if (cnt n - 1) { ans | (1 bit); cur nxt; } } return ans; }这段代码是整个解法的骨架。注意里面用vector来维护当前边集每次筛选后如果可行就替换。这样做的好处是逻辑清晰缺点是每次都要复制vector在m很大的时候会有性能开销。如果追求极致可以用一个标记数组来标记哪些边还在当前边集中避免复制。但一般来说m在1e5以内时vector的复制开销是可以接受的。4. 复杂度分析与常见错误排查这个算法的时间复杂度是O(30 * (m n))因为每一位都要遍历当前边集跑一次Kruscal而Kruscal的复杂度主要取决于边数和并查集操作。30是常数所以整体可以认为是O(m n)的级别对于1e5的数据规模来说完全够用。空间复杂度是O(m n)主要是存储边和并查集数组。常见错误我整理了一个速查表这些都是我在做题和帮别人debug时反复见到的错误类型具体表现原因修正方法并查集未重置答案偏小或偏大上一位的连通状态污染了当前位每次尝试新位前重新初始化parent数组边集更新时机错误答案偏小在不能形成生成树时也更新了边集只在cntn-1时更新cur枚举位数不够答案偏小边权超过2^30时高位丢失根据最大边权确定起始位生成树判定条件错误答案偏大用cntn-1判断但cnt不可能超过n-1严格判断cntn-1递归find爆栈运行时错误数据量大时递归深度过深改用迭代版本find其中并查集未重置是最容易犯的错因为写惯了模板题的人会下意识觉得并查集初始化一次就行。但这道题的逻辑决定了每次尝试新位都是一次独立的连通性判定必须重置。还有一个细节如果图本身不连通那么无论怎么选边都无法形成生成树最终答案应该是0。这个情况在代码里自然处理了——因为cnt永远达不到n-1ans保持为0。但有些题目会保证图连通所以这个边界情况不一定能测到自己写的时候心里要有数。5. 从这道题延伸出的思考这道题表面上是Kruscal加并查集的组合但真正有意思的地方在于它把最小生成树的框架用在了按位与最大这个完全不同的目标上。Kruscal在这里退化成了一个连通性判定工具排序步骤被完全跳过这是很多人一开始想不到的。我后来在想如果目标改成按位或最大或者按位异或最大思路会不会不一样按位或的话高位优先的性质依然成立但贪心策略可能需要调整因为或运算下某一位为1的边可能不止一条选择哪一条会影响后续。按位异或就更复杂了因为异或没有单调性高位优先不成立得换别的思路。这些问题没有标准答案但拿来锻炼思维挺好的。另外这道题也提醒我模板题做多了容易形成思维定式。看到生成树就想到Kruscal看到Kruscal就想到排序但实际题目往往会在某个环节做变形。真正重要的是理解每个算法组件解决的是什么问题——Kruscal解决的是在边集上选出一棵生成树并查集解决的是动态连通性判定至于边怎么排序、按什么顺序加那是可以根据题目目标灵活调整的。我在实际写这道题的时候第一次提交WA了就是因为并查集没有重置。后来改过来之后又发现枚举位数只到20导致大权值的边被忽略。这两个坑踩完之后这道题才算真正吃透了。现在回头看这道题的价值不在于它有多难而在于它逼着你去思考每个算法步骤的必要性——排序真的必要吗并查集的状态能不能复用这些问题想清楚了对Kruscal和并查集的理解会上一个台阶。

相关新闻

JSTL 实战指南:从 JSP 脚本到标签化视图的改造与避坑

JSTL 实战指南:从 JSP 脚本到标签化视图的改造与避坑

1. 为什么今天还要回头啃 JSTL刚入行那会儿&#xff0c;我接手的第一个老项目就是一套典型的 JSP Servlet 架构&#xff0c;页面里到处是<% %>脚本片段&#xff0c;一个列表页能塞进去两百多行 Java 代码&#xff0c;改一个字段要在 HTML 和 Java 之间来回跳。后来带我的…

2026/10/12 18:14:04 阅读更多 →
Web前端入门指南:三件套基础、调试思维与浏览器语音处理

Web前端入门指南:三件套基础、调试思维与浏览器语音处理

看到“32-33.Web前端入门”这个标题&#xff0c;我第一反应是&#xff1a;这多半是个系列课程里的某一阶段小结。因为真正的“Web前端入门”不是报个班刷完就好&#xff0c;而是要在合适的时间点把知识、工具、调试思维串起来。这篇文章我就按自己带新人时最常用的一套节奏来讲…

2026/10/12 18:14:03 阅读更多 →
电力负荷预测深度学习实战:LSTM/GRU/Transformer 七模型源码与避坑指南

电力负荷预测深度学习实战:LSTM/GRU/Transformer 七模型源码与避坑指南

简介&#xff1a;基于深度学习算法实现电力负荷时间序列未来预测的Python项目源码&#xff0c;面向计算机、数据科学、人工智能等专业在校生与从业者&#xff0c;既可作课程设计、毕业设计&#xff0c;也适合深度学习时序建模入门与实践。代码包含LSTM、GRU、Transformer、ARIM…

2026/10/12 18:14:03 阅读更多 →

最新新闻

VSCode调试Linux coredump:从系统配置到图形化分析实战

VSCode调试Linux coredump:从系统配置到图形化分析实战

服务器半夜收到某个核心服务的崩溃告警&#xff0c;机器上只留了一个几百MB的coredump文件&#xff0c;可执行文件的debug符号也还在。大部分人的第一反应是gdb program core&#xff0c;然后敲bt&#xff0c;凑合看个函数名。但如果你已经习惯在VSCode里写代码&#xff0c;完全…

2026/10/13 2:54:39 阅读更多 →
Windows部署Docker完整指南:WSL2与Docker Desktop实战避坑

Windows部署Docker完整指南:WSL2与Docker Desktop实战避坑

最近在Windows机器上把Docker完整部署了一遍&#xff0c;踩了不少坑&#xff0c;也总结出一些经验。网上关于Windows部署Docker的教程很多&#xff0c;但大多是零散片段&#xff0c;要么只讲安装&#xff0c;要么只讲某个报错&#xff0c;真正把“为什么这样部署”“每一步背后…

2026/10/13 2:54:39 阅读更多 →
5W无线充电发射端方案全解析:IP6825芯片、A11/A11a线圈与FOD调校

5W无线充电发射端方案全解析:IP6825芯片、A11/A11a线圈与FOD调校

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 2:54:39 阅读更多 →
STM32F429移植RT-Thread nano与FinSH实战指南

STM32F429移植RT-Thread nano与FinSH实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 2:54:39 阅读更多 →
告别重复劳动:四款小众插件实测,批量处理与网页自动化让效率翻倍

告别重复劳动:四款小众插件实测,批量处理与网页自动化让效率翻倍

每天打开电脑&#xff0c;总有一堆活儿在等着&#xff1a;给几百个文件改格式、把同一段代码复制到不同位置、在网页里反复填写一模一样的表格、从聊天记录和文档之间来回搬运信息。这些事本身不难&#xff0c;难的是它们又碎又多&#xff0c;一天下来光忙这些&#xff0c;真正…

2026/10/13 2:54:39 阅读更多 →
AnyPS5:用WOL+网页控制面实现PS5远程唤醒与串流调度

AnyPS5:用WOL+网页控制面实现PS5远程唤醒与串流调度

1. 为什么会有 AnyPS5&#xff1a;主机留在客厅&#xff0c;时间却都花在路上先说结论&#xff1a;AnyPS5 不是模拟器&#xff0c;也不是什么越狱工具&#xff0c;它是我给自己家用主机搭的一个远程使用入口——一个网页控制面&#xff0c;加上一套围绕开机、唤醒、状态探测和串…

2026/10/13 2:53:39 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目&#xff0c;我最大的感受是&#xff0c;大部分坑不是出在技术前沿&#xff0c;而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节&#xff0c;最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了&#xff0c;很难不碰到舵机控制这道坎。做智能小车要转方向&#xff0c;做机械臂要抬关节&#xff0c;做云台要偏航俯仰&#xff0c;背后都是同一个东西在干活&#xff1a;舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”&#xff1f;——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台&#xff1f;或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜&#xff1f;又或者&#xff0c;在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介&#xff1a;基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码&#xff0c;面向计算机相关专业课程设计与期末大作业学生&#xff0c;以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程&#xff0c;…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程&#xff1a;键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化&#xff0c;十个新手有八个栽在"往输入框里填东西"这件事上&#xff1a;要么填不进去&#xff0c;要么填了一半&#xff0c;要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程&#xff1a;阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀&#xff1a;什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面&#xff0c;跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →