【bzoj4245】[ONTAK2015]OR-XOR 贪心

题目描述

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

输入

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

输出

输出一个整数,即总费用的最小值。

样例输入

3 2
1 5 7

样例输出

3


题解

贪心

还是那句话:100000(2)>011111(2)

所以想让总数最小,应该尽量让高位为0。

而如果a or b or c or ... = 0,则一定是a、b、c、...都等于0。

于是就转化为让某一段的xor为0.

考虑xor的性质,a xor a = 0。

根据这个性质可以使用前缀和处理这种xor问题。

具体的,xor(i,j)=xor(1,i-1)^xor(1,i-1)^xor(i,j)=sum[i-1]^sum[j]。

而题目要求需要分出m段,所以某一位为0的充分必要条件是sum[n]=0,且满足sum[i]=0&&tag[i]=0(见下行)的i的个数>=m。

这样能够使得该位为0。而该位已经是0了,破坏条件的都不能选,所以应把sum[i]≠0的i打上标记,表示不能再选择。

最后被迫选的1的和就是答案。

#include <cstdio>
#include <algorithm>
using namespace std;
long long sum[500010] , x , ans;
bool tag[500010];
int main()
{
    int n , m , i , j;
    scanf("%d%d" , &n , &m);
    for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &x) , sum[i] = sum[i - 1] ^ x;
    for(i = 62 ; ~i ; i -- )
    {
        int num = 0;
        for(j = 1 ; j <= n ; j ++ )
            if(!(sum[j] & (1ll << i)) && !tag[j])
                num ++ ;
        if(sum[n] & (1ll << i) || num < m) ans += (1ll << i);
        else
            for(j = 1 ; j <= n ; j ++ )
                if(sum[j] & (1ll << i))
                    tag[j] = 1;
    }
    printf("%lld" , ans);
    return 0;
}

 

 

posted @ 2017-06-02 21:04  GXZlegend  阅读(298)  评论(0编辑  收藏  举报