• BZOJ 4245: [ONTAK2015]OR-XOR 贪心 + 位运算


    Description

    给定一个长度为n的序列a[1],a[2],...,a[n],请将它划分为m段连续的区间,设第i段的费用c[i]为该段内所有数字的异或和,则总费用为c[1] or c[2] or ... or c[m]。请求出总费用的最小值。

    Input

    第一行包含两个正整数n,m(1<=m<=n<=500000),分别表示序列的长度和需要划分的段数。
    第一行包含n个整数,其中第i个数为a[i](0<=a[i]<=10^18)。

    Output

    输出一个整数,即总费用的最小值。
    题解 $Rightarrow$
    $ans=Min(异或和1|异或和2|....异或和$m$)$.
    依次从高位向低位枚举 :
    根据二进制的性质,一个高位为 $0$ 肯定是要优于其后面的低位全部为 $0$ 的.
    那么判断一个最高为可不可以为 $0$ 就是判断可不可以将 $n$ 个 0/1 数字分成 $m$ 段,其中每一段的异或和都为 $0$.
    这等价于大于等于 $m$ 个前缀异或值为 $0$.
    这个只需依次从 $1$ 到 $n$ 依次扫一遍前缀异或值即可,可以证明,这么做一定是最优的.
    那么,还需满足的一个条件就是 $sum[n]$ 一定要等于 $0$ (因为这是对应最后一段的异或值).
    先枚举 $64$ 位,再依次枚举 $1$ 到 $n$ ,并把一些前缀异或值不等于 $0$ 的打上标记,下一次循环时不能选即可
    时间复杂度位 $O(64 imes n)$ 
    #include <bits/stdc++.h>
    #define ll long long 
    #define setIO(s) freopen(s".in","r",stdin) 
    using namespace std; 
    
    const int maxn=500005; 
    
    ll arr[maxn],sum[maxn];  
    int flag[maxn];   
    int main() { 
        // setIO("input");  
        int n,m; 
        ll ans=0; 
        scanf("%d%d",&n,&m); 
        for(int i=1;i<=n;++i) scanf("%lld",&arr[i]), sum[i]=sum[i-1]^arr[i];    
        for(int i=62;i>=0;--i) {
            int cnt=0;   
            for(int j=1;j<=n;++j) if(!flag[j]&&(sum[j]&(1ll<<i))==0) ++cnt;       
            if(cnt>=m&&(sum[n]&(1ll<<i))==0) { 
                for(int j=1;j<=n;++j) {
                    if(!flag[j]&&((1ll<<i)&sum[j])) flag[j]=1;        
                }
            }                
            else ans|=(1ll<<i);             
        }
        printf("%lld
    ",ans); 
        return 0; 
    }
    

      

  • 相关阅读:
    你有认真了解过自己的“Java对象”吗? 渣男
    布隆过滤器,你也可以处理十几亿的大数据
    阻塞队列——手写生产者消费者模式、线程池原理面试题真正的答案
    Java集合面试题汇总篇
    Github 骚操作
    责任链模式——更灵活的if else
    时间复杂度到底怎么算
    创造DotNet Core轻量级框架【二】
    创造DotNet Core轻量级框架【一】
    小胖李的面试之旅(二)
  • 原文地址:https://www.cnblogs.com/guangheli/p/11276341.html
Copyright © 2020-2023  润新知