CF 1592E. Bored Bakry
2400_1592E. Bored Bakry
题意
给定长度为 \(n\) 的序列 \(a\) ,求出使得 \(a_l \& a_{l+1} \& ... \& a_r > a_l \bigoplus a_{l+1} \bigoplus ... \bigoplus a_r\) 成立的最长子数组的长度。
分析
假设满足条件的子数组 \([l, r]\) 的 \(\&\) 值为 \(X\), \(\bigoplus\) 值为 \(Y\) 。
那么 \(X > Y\) 成立的条件是存在某位 \(k\) ,使得大于 \(k\) 位的 \(X = Y\) ,在 \(k\) 位上 \(X > Y\) ,后面的随意。
首先可以得到这个子数组长度一定是偶数。
因为必须要存在一个位使 \(X > Y\) ,即 \(X_k = 1, Y_k = 0\) 。
如果子数组长度为奇数,\(X_k = 1\) 则每个数的 \(k\) 位都为 \(1\) ,而这样 \(Y_k\) 也一定为 \(1\) 。这样一定是不满足的。
所以,满足条件的子数组 \([l, r]\) 满足:
- 数组长度为偶数。
- 数组中所有数字满足某一位 \(k\) 上都为 \(1\) 。这样只需要判断 \([l, r]\) 上高于 \(k\) 位上的 \(xor\) 值和为0即可。这样主要条件就能满足。
如何得到 \([l, r]\) 上的高于 \(k\) 位上的 \(xor\) 值和?只需要用异或前缀和即可。
枚举每个位 \(d\) ,假设满足条件的子数组在这个位上都为 \(1\) 。
用桶存储每个满足前缀异或和为 \(0\) 的位置,由于长度要偶数,因此每个桶内需要分别维护奇数长度和偶数长度的位置。
在从前往后枚举的过程中,如果遇到某一个数字在 \(d\) 位上为 \(0\) ,那么数组断开,前面的数据已经没有意义了,需要清零。从这一位重新开始算。
Code
/* Storms make trees take deeper roots.*/
#include <bits/stdc++.h>
using namespace std;
const int N = 100001;
int a[N], c[N];
void solve ()
{
int n, ans = -1; cin >> n;
for (int i = 1; i <= n; i ++ ) cin >> a[i];
for (int d = 0; d <= 20; d ++ )
{
vector<int> saved[N]; // 桶维护每个异或和的奇偶位置
saved[0].push_back(0);
int prt = 0, pre = 0; // pre xor前缀和
for (int i = 1; i <= n; i ++ )
{
pre ^= (a[i] >> (d + 1)); // 找出数字中位高于k的数字
c[i] = pre; // 存储为前缀异或和
if ((a[i] >> d) & 1)
{
// 存储奇数和偶数,只需要两个数字即可
if (saved[pre].size() < 2)
{
if (saved[pre].empty()) saved[pre].push_back(i);
else if ((saved[pre][0] ^ i) & 1) saved[pre].push_back(i);
}
for (auto it : saved[pre])
{
if (((it ^ i) & 1) == 0) ans = max(ans, i - it);
}
}
// 在d位上为0,清空之前的数据
else
{
while (prt < i)
{
if (prt == 0) saved[0].clear();
saved[c[prt++]].clear();
}
saved[c[i]].push_back(i);
}
}
}
cout << ans << endl;
}
signed main ()
{
// cout.tie(0)->sync_with_stdio(0);
// int _; for (cin >> _; _--; ) solve();
solve();
return 0;
}