洛谷 P14359 [CSP-J 2025] 异或和

洛谷 P14359 [CSP-J 2025] 异或和

题目大意

原题链接:洛谷 P14359 [CSP-J 2025] 异或和

有一个长度为 \(n\) 的非负整数序列 \(a_1, a_2, \dots, a_n\)。定义一个区间 \([l, r]\) (\(1 \leq l \leq r \leq n\)) 的权值为 \(a_l, a_{l+1}, \dots, a_r\) 的二进制按位异或和,即 \(a_l \oplus a_{l+1} \oplus \dots \oplus a_r\),其中 \(\oplus\) 表示二进制按位异或。选择序列中尽可能多的不相交的区间,使得每个区间的权值均为 \(k\)。两个区间 \([l_1, r_1], [l_2, r_2]\) 相交当且仅当两个区间同时包含至少一个相同的下标,即存在 \(1 \leq i \leq n\) 使得 \(l_1 \leq i \leq r_1\) 且 \(l_2 \leq i \leq r_2\)。输出能选出的区间数量的最大值。

题意分析

异或的定义

异或是一种逻辑运算或位运算,记作 \(a \oplus b\),C++中写作 a^b。

当异或作为逻辑运算时,表示两个条件是否刚好一个为真、另一个为假。其真值表如下:

\(a\) \(b\) \(a \oplus b\)
\(0\) \(0\) \(0\)
\(0\) \(1\) \(1\)
\(1\) \(0\) \(1\)
\(1\) \(1\) \(1\)

当异或作为位运算时,表示将两个数按位对齐,在每一位上进行逻辑异或运算得到的结果。显然题目中的异或运算指的是后者。

异或的性质

异或运算有两个特殊的性质。

  1. 若 \(a \oplus b = c\),则 \(a = c \oplus b\)。即异或的逆运算还是异或。
  2. \(a \oplus a = 0\),且 \(0 \oplus a = a\)。

思路分析

这道题可以使用类似前缀和的方法来做。

设 \(s_i = a_1 \oplus a_2 \oplus \dots \oplus a_i\)。特别的,\(s_0 = 0\)。则可以得到递推式 \(s_i = s_{i-1} \oplus a_i\)。

显而易见,对于区间 \([l, r]\) 的异或和 \(a_l \oplus a_{l+1} \oplus \dots \oplus a_r = s_r \oplus s_{l-1}\)。若 \(s_r \oplus s_{l-1} = k\),则 \(s_{l-1} = s_r \oplus k\)。所以可以枚举右端点 \(r\),并计算出 \(s_{l-1}\) 的值,取它最后一个出现的位置。如果这个位置没有被上一个区间包含,则将它作为左端点。

为什么是最后一个位置呢?如果最后一个位置都被上一个区间包含了,那么前面的位置也会被包含;如果最后一个区间没有被包含,前面的位置也没有被包含,那么不管选择哪一个位置,区间总数量都是一样的。

如何实现呢?可以使用一个 pos 数组,pos[i] 表示元素 i 在 s 数组中最后一次出现的位置。使用变量 lts 记录上一个区间的右端点。如果 pos[i]<lts,则说明左端点被上一个区间包含。注意是小于,因为计算的左端点是 \(l-1\),所以这是左开右闭区间,即使 pos[i]==lts,i 也是合法的左端点。

pos 数组的长度取决于 s[i] 的大小,而 s[i] 一定小于 \(2^{20} = 1048576\),所以数组开的比它稍大就可以了。

AC 代码

#include <cstdio>
#include <cstring>
using namespace std;

int n, k;
int a[500010], s[500010];
int pos[1050000];

int main() {
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        s[i] = s[i - 1] ^ a[i];
    }
    memset(id, -1, sizeof(id));
    pos[0] = 0;
    int lts = 0, ans = 0;
    for (int r = 1; r <= n; r++) {
        int x = s[r] ^ k;
        if (pos[x] >= lts) {
            ans++;
            lts = r;
        }
        pos[s[r]] = r;
    }
    printf("%d\n", ans);
    return 0;
}
posted @ 2026-09-24 17:04  zzx321  阅读(48)  评论(0)    收藏  举报