MT3019 异或和的或

文章描述了一种利用异或运算和前缀和的方法,解决给定整数数组中如何通过合并区间使得异或和为特定数量的最小值的问题。通过贪心原则,找到分割点并确定最终结果。
摘要由CSDN通过智能技术生成

知识

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值