[CSP-J 2025] 异或和题解
P14359 [CSP-J 2025] 异或和 / xor(民间数据)
题目描述
小 R 有一个长度为 nnn 的非负整数序列 a1,a2,…,ana_1, a_2, \dots, a_na1,a2,…,an。定义一个区间 [l,r][l, r][l,r] (1≤l≤r≤n1 \leq l \leq r \leq n1≤l≤r≤n) 的权值为 al,al+1,…,ara_l, a_{l+1}, \dots, a_ral,al+1,…,ar 的二进制按位异或和,即 al⊕al+1⊕⋯⊕ara_l \oplus a_{l+1} \oplus \dots \oplus a_ral⊕al+1⊕⋯⊕ar,其中 ⊕\oplus⊕ 表示二进制按位异或。
小 X 给了小 R 一个非负整数 kkk。小 X 希望小 R 选择序列中尽可能多的不相交的区间,使得每个区间的权值均为 kkk。两个区间 [l1,r1],[l2,r2][l_1, r_1], [l_2, r_2][l1,r1],[l2,r2] 相交当且仅当两个区间同时包含至少一个相同的下标,即存在 1≤i≤n1 \leq i \leq n1≤i≤n 使得 l1≤i≤r1l_1 \leq i \leq r_1l1≤i≤r1 且 l2≤i≤r2l_2 \leq i \leq r_2l2≤i≤r2。
例如,对于序列 [2,1,0,3][2, 1, 0, 3][2,1,0,3],若 k=2k = 2k=2,则小 R 可以选择区间 [1,1][1, 1][1,1] 和区间 [2,4][2, 4][2,4],权值分别为 222 和 1⊕0⊕3=21 \oplus 0 \oplus 3 = 21⊕0⊕3=2;若 k=3k = 3k=3,则小 R 可以选择区间 [1,2][1, 2][1,2] 和区间 [4,4][4, 4][4,4],权值分别为 1⊕2=31 \oplus 2 = 31⊕2=3 和 333。
你需要帮助小 R 求出他能选出的区间数量的最大值。
输入格式
输入的第一行包含两个非负整数 n,kn, kn,k,分别表示小 R 的序列长度和小 X 给小 R 的非负整数。
输入的第二行包含 nnn 个非负整数 a1,a2,…,ana_1, a_2, \dots, a_na1,a2,…,an,表示小 R 的序列。
输出格式
输出一行一个非负整数,表示小 R 能选出的区间数量的最大值。
输入输出样例 #1
输入 #1
4 2
2 1 0 3
输出 #1
2
输入输出样例 #2
输入 #2
4 3
2 1 0 3
输出 #2
2
输入输出样例 #3
输入 #3
4 0
2 1 0 3
输出 #3
1
说明/提示
【样例 1 解释】
小 R 可以选择区间 [1,1][1, 1][1,1] 和区间 [2,4][2, 4][2,4],异或和分别为 222 和 1⊕0⊕3=21 \oplus 0 \oplus 3 = 21⊕0⊕3=2。可以证明,小 R 能选出的区间数量的最大值为 222。
【样例 2 解释】
小 R 可以选择区间 [1,2][1, 2][1,2] 和区间 [4,4][4, 4][4,4],异或和分别为 1⊕2=31 \oplus 2 = 31⊕2=3 和 333。可以证明,小 R 能选出的区间数量的最大值为 222。
【样例 3 解释】
小 R 可以选择区间 [3,3][3, 3][3,3],异或和为 000。可以证明,小 R 能选出的区间数量的最大值为 111。注意:小 R 不能同时选择区间 [3,3][3, 3][3,3] 和区间 [1,4][1, 4][1,4],因为这两个区间同时包含下标 333。
【样例 4】
见选手目录下的 xor/xor4.inxor/xor4.inxor/xor4.in 与 xor/xor4.ansxor/xor4.ansxor/xor4.ans。
该样例满足测试点 4,54, 54,5 的约束条件。
【样例 5】
见选手目录下的 xor/xor5.inxor/xor5.inxor/xor5.in 与 xor/xor5.ansxor/xor5.ansxor/xor5.ans。
该样例满足测试点 9,109, 109,10 的约束条件。
【样例 6】
见选手目录下的 xor/xor6.inxor/xor6.inxor/xor6.in 与 xor/xor6.ansxor/xor6.ansxor/xor6.ans。
该样例满足测试点 14,1514, 1514,15 的约束条件。
【数据范围】
对于所有测试数据,保证:
- 1≤n≤5×1051 \leq n \leq 5 \times 10^51≤n≤5×105, 0≤k<2200 \leq k < 2^{20}0≤k<220;
- 对于所有 1≤i≤n1 \leq i \leq n1≤i≤n,均有 0≤ai<2200 \leq a_i < 2^{20}0≤ai<220。
::cute-table{tuack}
| 测试点编号 | n≤n \leqn≤ | kkk | 特殊性质 |
|---|---|---|---|
| 111 | 222 | =0=0=0 | A |
| 222 | 101010 | ≤1\leq 1≤1 | B |
| 333 | 10210^2102 | =0=0=0 | A |
| 4,54, 54,5 | ^ | ≤1\leq 1≤1 | B |
| 6∼86 \sim 86∼8 | ^ | ≤255\leq 255≤255 | C |
| 9,109, 109,10 | 10310^3103 | ^ | ^ |
| 11,1211, 1211,12 | ^ | <220< 2^{20}<220 | 无 |
| 131313 | 2×1052 \times 10^52×105 | ≤1\leq 1≤1 | B |
| 14,1514, 1514,15 | ^ | ≤255\leq 255≤255 | C |
| 161616 | ^ | <220< 2^{20}<220 | 无 |
| 171717 | 5×1055 \times 10^55×105 | ≤255\leq 255≤255 | C |
| 18∼2018 \sim 2018∼20 | ^ | <220< 2^{20}<220 | 无 |
特殊性质 A: 对于所有 1≤i≤n1 \leq i \leq n1≤i≤n,均有 ai=1a_i = 1ai=1。
特殊性质 B: 对于所有 1≤i≤n1 \leq i \leq n1≤i≤n,均有 0≤ai≤10 \leq a_i \leq 10≤ai≤1。
特殊性质 C: 对于所有 1≤i≤n1 \leq i \leq n1≤i≤n,均有 0≤ai≤2550 \leq a_i \leq 2550≤ai≤255。
思路
稍微有些难度,首先,可以发现,我们可以统计以每个数开头的异或和为k的最小结尾,然后后缀取最小不断跳即可。
思考如何求,首先vector储存从1开始的前缀异或,然后对于每个数,求出前缀多少的才能使它达标,接着二分即可。
代码见下
#include<bits/stdc++.h>
using namespace std;
long long n,k,a[500005],b[500005],c[500005],d,br=1,op=0;
vector<long long> v[2000006];
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
b[i]=(b[i-1]^a[i]);
v[b[i]].push_back(i);
}
for(int i=1;i<=n;i++){
d=k^b[i-1];
c[i]=n+1;
if(v[d].size()!=0){
long long l=0,r=v[d].size()-1,md=n+1;
while(l<=r){
long long mid=(l+r)/2;
if(v[d][mid]>=i){
md=min(md,v[d][mid]);
r=mid-1;
}
else{
l=mid+1;
}
}
c[i]=md;
}
}
c[n+1]=n+1;
for(int i=n;i>=1;i--){
c[i]=min(c[i],c[i+1]);
}
br=1;
while(1){
br=c[br];
if(br>=n+1){
break;
}
br++;
op++;
if(br>=n+1){
break;
}
}
cout<<op<<endl;
return 0;
}

浙公网安备 33010602011771号