评价:我有异或症。
收集了一些跟异或相关的 trick,主要是以题目的形式放出来,做到了新的会再加。
由于本人不太会线性基,正在考虑要不要改成 01-Trie 学习笔记,相关题目暂时应该是没有,主要考虑异或本身的性质。
P4551 最长异或路径
题目链接
设 表示根到 路径上所有边权的异或和,众所周知异或有个很好的性质就是 ,那么根据这点我们不难发现: 路径上的边权异或和就等于 ,因为 LCA 向上的边权都异或两次消去了。
现在的问题就变成:找出最大的一个点对 ,使得 最大。
将所有 从高位到低位插入到 Trie 中,然后枚举 ,贪心地从 Trie 上向下走找到最大的 即可。具体来说就是每次看看能不能朝相反的那一位走,因为是从高位到低位插入这样一定是最优的。这个是非常基础的,相信大家都会。
时间复杂度 。
P9745 树上异或
题目链接
插播一个 dp 题。
朴素的想法是设 表示 所在的连通块权值为 时(只考虑子树内),子树内其他连通块的贡献之和, 表示 所在子树的答案。
的转移就考虑每个儿子到自己的边断不断:
其实就求个和:
时间复杂度 ,肯定过不去。
异或的一个很好的性质就是运算过程中每一位之间都是独立的,不妨按位考虑,也就是拆位。
设 表示 所在连通块权值的第 位为 时(同样也只考虑子树内),子树内其他连通块的贡献之和, 表示 所在子树的答案。
转移其实大差不差:
哦,还有 改成这样:
时间复杂度 。
P5283 异或粽子
题目链接
对 做一个异或前缀和,也就是记 ,那么区间 的异或和就可以转化为 ,特别规定 即可。
于是问题就转化成了:给你一个序列 ,求前 大的两两异或值之和。
注意这里要求 时有 ,这是比较烦人的,但我们可以直接将 的限制去掉,转而求前 大的两两异或值,然后再将答案除以 就好了,因为发现这样只是把 和 重复算了一遍(由于有 , 的情况不需要任何特殊处理)。
事实上强制带 的限制也可以写可持久化 Trie 来解决。
考虑给定其中一个 时,能不能求出来第 大的 。其实是简单的,Trie 树上每个点维护子树内有几个数,把 扔到 Trie 上去进行一个类似于线段树上二分的过程就好了,当一位填 后最小排名不会超过 时就贪心填 。
然后类比一下超级钢琴的做法,开一个小根堆,对于每个 先将最小的 扔进堆里,这样就能保证堆顶一定是当前能取的最小值。接下来每次取出堆顶后,假如这个堆顶对它的 来说是第 小的,那就用上面方法把第 小的扔进去,这样重复 次之后我们就成功求出前 大的两两异或值了。
于是就做完了,时间复杂度 。
CF241B Friends
题目链接
乍一看这不就是异或粽子吗?但是发现 非常大,而上面做法的复杂度是带个 的,过不去。
其实我们还有一个跟 无关的 做法。
首先还是先把 乘上 ,这些就不多说了。然后我们分为两部分去完成:求出第 大的异或值 ,求出大于等于 的异或值之和。
对于第一部分,考虑二分答案 ,判断 是否小于等于 。不难发现此时我们只需要求出有多少对 满足 就行了,可以直接把每个 扔到 Trie 树上,类似线段树二分的过程(或者你也可以理解成枚举 LCP),如果遇到 某位是 ,那么与 这位相反的那棵子树内的 就都是合法的,借此求出有多少个满足条件的 即可。这部分是两个 的。
对于第二部分,考虑在 Trie 树上每个点 额外维护一个 表示经过 的 中有几个二进制下第 位是 ,这样把 扔到 Trie 树上走的过程中,如果遇到 某位是 ,那么与 这位相反的那个子树内的数与 异或后就一定是大于 的,相当于是求 异或其它经过这棵子树的 的和,贡献可以直接拆位算出来。
这样的话时空都是两个 的,可以通过,但是空间其实可以去掉一个 。
考虑将 从小到大排序,有这么个性质:经过 Trie 上某个结点的 一定是一段连续的区间。
因此可以直接对原序列拆位做一个前缀和,需要统计某棵子树的贡献拆位时可以对应其在序列中的左右端点,差分计算贡献。
这样就是时间两个 ,空间单 了。
ABC252Ex K-th beautiful Necklace
题目链接
很好的神仙题!技巧性很强的题,感觉就是各种 trick 的杂糅。
下文中令 为原题中的 。发现这个 的范围是很神奇的,让人很容易去想暴力的可行性。当 确定时直接爆搜的上界显然是 的,那么整体的上界具体到底是多少呢?事实上 时会取到 左右。
直接搜显然是爆炸了,但是注意到如果折半的话那搜一半的复杂度就会开个根号,只有 的级别(上界其实是 左右),这是完全可以接受的,因此可以贪心地将颜色划分为大小乘积比较接近的两组,折半地将两组的选法先搜出来。
假设第一组搜出来的所有选法权值扔到序列 中,第二组搜出来的所有选法权值扔到序列 中,那么现在问题就转化成了:给你两个序列 和 ,求 的第 大值。
一个直接的想法是二分答案 ,把每个 扔到 Trie 中,然后对于每个 计算有多少 满足 即可。分析下复杂度,我们令 表示搜一半的复杂度上界,那么时间复杂度为 ,算下来快到 ,好的看来是似了。
考虑干掉二分的 :方法是直接让所有的 同时在 Trie 上移动。具体地,从高到低枚举答案每一位,计算每个 在当前位的相反方向上的子树内的 个数并求和,如果大于 说明答案这一位为 ,集体向该位相反方向移动即可,否则说明答案这一位为 ,全部向当前位的同方向移动,并将 减去刚刚算的个数即可。
现在的时间复杂度就是 的了,真的赢了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现