知识
1.或运算:有一个数为1,结果为1
所以贪心:尽量让高位为0(从高位到低位),这样就可以求e的最小值
2.异或:相同为0,不同为1(1有奇数个,则值为1;1有偶数个,则值为0)
选分割点时,如果前面的区间异或和为0,则从此处分开(因为贪心规则是尽量让高位为0)
即如果求得异或和为0的区间>m个,则可以合并某些区间来使异或和为0的区间=m个,这样使e为最小值0。
如果求得异或和为0的区间<m个
3.前缀和:a[i]存数,b[i]存异或和的前缀和。遍历位数。
若b[i-1]=0,则下一个区间不用减去i-1部分,即直接使用b[i]即可。(找一些分割点使区间为0)
再找一些不可以的分割点,这样之后的遍历就可以跳过了。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5 + 10;
int n, m, ans;
int a[N],b[N];
bool st[N]; // 记录不可分割点
signed main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 1; i <= n; i++)
{
b[i] = b[i - 1]^a[i];
}
for (int i = 62; i >= 0; i--)
{ // 遍历每一位(62是根据a的数据范围得出的)
int cnt = 0; // 分割点个数
for (int j = 1; j <= n; j++)
{
if (!st[j] && !(b[j] & (1LL << i))) // 分割点使可行的并且这一区间为0
cnt++;
}
if (cnt >= m && !(b[n] & (1LL << i))) // 此位为0
// cnt>=m:分割点>=m,所以结果为0
{ // 看整个的异或和。即看总共异或和是否为0,若不为0则说明有奇数个1,最后结果不可能为0
for (int j = 1; j <= n; j++)
{ // 看哪些点是不可分割点
if (b[j] & (1LL << i))
{
st[j] = 1;
}
}
}
else
{ // 此位为1
ans |= 1ll << i;
}
}
cout << ans;
}