当前位置 博文首页 > Keven_11的博客:C++题解:位运算

    Keven_11的博客:C++题解:位运算

    作者:[db:作者] 时间:2021-08-18 15:47

    ??????目录

    题目?

    题解


    题目?

    • ?1000ms
    • ?131072K

    蒜头君有一个包含?n?个数的数组,他想把这个数组分成?m?段,然后在每一段里求出所有数异或的结果,然后再把这?m?个数求按位或以后的结果。

    蒜头君想知道他想要的结果最小可以是多少。

    输入格式

    输入第一行包含两个整数?n,m(1≤m≤n≤5×105)?,分别表示数组的大小和段数。

    第二行包含?n?个整数?ai?(0≤ai?≤1018)?,表示数组中的每个数。

    输出格式

    表示最小的结果。

    输出时每行末尾的多余空格,不影响答案正确性

    要求使用「文件输入输出」的方式解题,输入文件为?bit.in,输出文件为?bit.out

    样例输入

    3 2
    1 5 7

    样例输出

    3

    题解:

    知识点:按位计算

    分析:?

    看到区间异或和,可以转化为两个前缀异或和的异或

    考虑或的性质,想要或起来结果最小,一定保证高位尽可能是0,实在不行才是1,所以可以从高位往低位按位贪心

    考虑每一位,想让这位是?0,必须所选的前缀异或这一位全是?0,看一下可用的前缀异或有多少个这一位为?0?的,要够?m?个,并且所有数的异或这一位也必须是?0(最后一段),如果可以,答案的这一位就可以是?0,然后把所有这一位为?1?的标记为不可用

    注意,我们不能局限在“分成?m?段”这个东西中,要把它抽象化

    代码:

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int NOIP=5e5+1;
    long long a[NOIP],ans;
    bool f[NOIP];
    int main(){
        freopen("bit.in","r",stdin);
        freopen("bit.out","w",stdout);
        int n,m;
        cin>>n>>m;
        for (int i=1;i<=n;i++){
            cin>>a[i];
            a[i]^=a[i-1];//前缀异或和
        }
        for (int i=62;i>=0;i--){//从高位往低位贪心
            int cnt=0;//这一位0的数量
            for (int j=1;j<=n;j++){
                if (!f[j] && !(a[j] & (1LL<<i))){//判断有多少个0
                    cnt++;
                }
            }//!(a[n] &(1LL<<i))是判断所有数的异或这一位是否是 0 ↓
            if (cnt>=m && !(a[n] &(1LL<<i))){//cnt>=m:这一位为0的a[i]的要够m个
                for (int j=1;j<=n;j++){
                    if (a[j] & (1LL<<i)){//把所有这一位为 1 的数标记为不可用
                        f[j]=true;
                    }
                }
            }else{
                ans|=1LL<<i;//答案的这一位就是 1
            }
        }
        cout<<ans<<endl;
        return 0;
    }

    cs
    下一篇:没有了