MT3019 异或和的或

简介: MT3019 异或和的或

知识

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)

再找一些不可以的分割点,这样之后的遍历就可以跳过了。

fe4ad03a6c48482ea09963b8684d7f61.jpg

f68d453d30a7453f95a86e23e49d9e41.jpg

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


相关文章
|
3天前
学习使用按位异或 ^
【6月更文挑战第22天】学习使用按位异或 ^。
8 1
MT2045 斐波那契,但是是字符串
MT2045 斐波那契,但是是字符串
|
1月前
|
C语言
每天一道C语言编程(2^k进制数)
每天一道C语言编程(2^k进制数)
10 0
|
1月前
|
C++
(C++)只出现一次的数字I--异或
(C++)只出现一次的数字I--异或
22 0