题目链接: Bored Bakry
大致题意
给定一个长度为 n n n的序列, 第 i i i个数字为 a i a_i ai.
现要求选择一个尽可能长的区间 [ l , r ] [l, r] [l,r], 满足 a l & a l + 1 & a l + 2 . . . & a r a_l \ \& \ a_{l+1} \& \ a_{l+2} ... \& \ a_r al & al+1& al+2...& ar > a l ⊕ a l + 1 ⊕ a l + 2 . . . ⊕ a r a_l \ \oplus \ a_{l+1} \oplus \ a_{l+2} ... \oplus \ a_r al ⊕ al+1⊕ al+2...⊕ ar
最终输出这个最大的区间长度.
解题思路
思维
首先我们选择的区间长度一定是偶数.
我们考虑左侧要严格大于右侧, 因此必然存在某个最高位 k k k, 使得左侧这位为1, 右侧为0.
即: 区间 [ l , r ] [l, r] [l,r]的每个数字, 第 k k k位都是1, 这样保证左侧第 k k k位为1. 而奇数个1异或, 结果也一定为1, 因此区间长度一定为偶数.
我们不妨假设第 k k k位为最高位的情况, 那么对于合法区间 [ l , r ] [l, r] [l,r], 比 k k k更高的所有位上, 一定异或和为 0 0 0.
不妨考虑枚举区间右端点 r r r, 对于当前异或和来寻找最小的合法左端点 l l l. 我们保证每个 [ l , r ] [l, r] [l,r]之间, 第 k k k位均为 1 1 1即可. 如果当前第 k k k位不为 1 1 1, 则我们清空记录信息的数组即可.
AC代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E6 + 10;
int a[N];
int odd[N], even[N]; //对于奇数(偶数)位置, 异或和为x的最小左端点.
int main()
{
int n; cin >> n;
rep(i, n) scanf("%d", &a[i]);
memset(odd, -1, sizeof odd), memset(even, -1, sizeof even);
int res = 0;
for (int k = 0; k < 20; ++k) {
vector<int> v(1, 0);
even[0] = 0;
int sum = 0; // 记录当前前缀和
rep(i, n) {
sum ^= a[i] >> (k + 1);
if (a[i] >> k & 1) {
if (i % 2 and odd[sum] != -1) res = max(res, i - odd[sum]);
else if (i % 2 == 0 and even[sum] != -1) res = max(res, i - even[sum]);
}
else {
for (auto& op : v) even[op] = odd[op] = -1;
v.clear();
}
if (i % 2 and odd[sum] == -1) odd[sum] = i;
else if (i % 2 == 0 and even[sum] == -1) even[sum] = i;
v.push_back(sum);
}
for (auto& op : v) even[op] = odd[op] = -1;
}
cout << res << endl;
return 0;
}