[BZOJ4245][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
输出一个整数,即总费用的最小值。
Sample Input
3 2
1 5 7
1 5 7
Sample Output
3
HINT
第一段为[1],第二段为[5 7],总费用为(1) or (5 xor 7) = 1 or 2 = 3。
Solution
毒瘤题
做法:贪心
我们要结果尽可能小,那么就要尽可能地让高位为0
先处理出前缀异或和,然后从高位往低位枚举,看看能否让这一位为0(即在前缀异或和中存在m个该位可以为0的,且总异或和这位也不为0)
然后如果能为0的话,就把前缀异或和中的所有不为0的位数标记一下,之后都不能再选了(如果选了可能你这一位就变成了1,然后高位为0显然比低位为0更优)
就这样去贪心就可以了
这题真的好神啊。。不看题解完全不会写
#include <bits/stdc++.h> using namespace std ; #define ll long long #define N 500010 int n , m ; ll a[ N ] , c[ N ] , ans = 0 ; bool vis[ N ] ; int main() { scanf( "%d%d" , &n , &m ) ; for( int i = 1 ; i <= n ; i ++ ) { scanf( "%lld" , &a[ i ] ) ; c[ i ] = c[ i - 1 ] ^ a[ i ] ; } for( int i = 62 ; i >= 0 ; i -- ) { int cnt = 0 ; for( int j = 1 ; j <= n ; j ++ ) { if( !vis[ j ] && ( c[ j ] & ( 1ll << i ) ) == 0 ) cnt ++ ; } if( cnt >= m && ( c[ n ] & ( 1ll << i ) ) == 0 ) { for( int j = 1 ; j <= n ; j ++ ) { if( ( c[ j ] & ( 1ll << i ) ) != 0 ) vis[ j ] = 1 ; } }else ans = ans | ( 1ll << i ) ; } printf( "%lld\n" , ans ) ; return 0 ; }