1 条题解
-
0
自动搬运
来自洛谷,原作者为

teylnol_evteyl
我是狸猫盘的猫 | 哇,Teylnol Evteyl! | 很慢地摇头搬运于
2025-08-24 22:49:57,当前版本为作者最后更新于2023-09-04 16:11:48,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这篇题解中,我将从线段树的角度重新讲解,同时提出一个扩展。
我们可以用权值线段树维护最长连续段:线段树每个节点维护 分别表示这个区间的左边连续段长度、右边连续段长度、最大连续段长度、区间长度,这样就可以实现区间合并。
这样建的线段树如图:

其中橙色线段代表左儿子,蓝色线段代表右儿子,下同。
为了方便后文叙述,“层”从下往上、从 开始编号,层的最大编号为 ,每一层的节点从 开始编号。
考虑一个异或 的操作,比如 ,如果固定底层的节点不变,则线段树会长这个样子:

可以发现,实际上是把第 层和第 层的节点交换左右儿子。
实际上,对于异或 的操作,若 二进制第 位(从低到高,从 开始编号)为 ,则将线段树第 层的左右儿子交换。
对于所有的异或 的操作,线段树的节点会有很多重合部分,把所有的操作的线段树建出来并合并,则会是这个样子:

有几个性质:
- 对于第 层第 个节点,它的左儿子是第 层第 个节点,右儿子是第 层第 个节点,其中 表示 和 的按位异或。
- 对于第 层第 个节点,以这个点为根的线段树是异或 操作后的线段树。
对于本题,第 层节点的 值便是答案。
实际上,由于是线段树,它可以维护区间合并。也就是说,下标异或的背景下,可以 求一般的可以区间合并的区间查询,但不支持快速修改(如图,一个底层节点涉及的节点是 的)。
比如有以下问题:
- 这题的基础上可以加一个值域区间的限制。
- 给定长度为 的序列 , 次询问给定 求 ,,,。
- 给定大小为 的集合 , 次询问给定 ,求 里的数异或上 后,集合的第 小值,,。
#include <bits/stdc++.h> using namespace std; const int M = 21, N = 1 << 19 | 5; int m = 19, n = 1 << m, q, t, x, y; struct node{ int l, r, m, p; }s[2][N]; void pushup(node &u, node &l, node &r) { u.l = l.p ? l.l + r.l : l.l; u.r = r.p ? r.r + l.r : r.r; u.m = max(l.r + r.l, max(l.m, r.m)); u.p = l.p & r.p; } int main() { scanf("%d%d", &q, &t); while(q -- ) scanf("%d", &x), s[0][x] = {1, 1, 1, 1}; for(int i = 1, x = 1, y = 0; i <= m; i ++ , x ^= 1, y ^= 1) for(int j = 0; j < n; j ++ ) pushup(s[x][j], s[y][j], s[y][j ^ (1 << i - 1)]); while(t -- ) scanf("%d", &x), y ^= x, printf("%d\n", s[1][y].m); return 0; }
- 1
信息
- ID
- 9063
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者