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

      

  • 相关阅读:
    PHP5中PDO的简单使用
    Apache中设置默认首页的方法
    PHP数组读取的循环操作
    header("Location:login.php")应该注意的几个问题
    纯JavaScript实现弹出选择第几个单选按钮
    PHP关于错误抑制符@的使用
    CSS盒子模型
    Appache中的ServerAlias
    PHP move_uploaded_file() 函数 定义和用法
    PHP ob_start() 函数介绍
  • 原文地址:https://www.cnblogs.com/guangheli/p/11276341.html
Copyright © 2020-2023  润新知