题目描述
给定一个长度为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; }