CODE FESTIVAL 2016 Grand Final: C - Cheating Nim

,

https://beta.atcoder.jp/contests/cf16-exhibition-final/tasks/cf16_exhibition_final_c

solution

数列$a$から異なる$k$項選んで$a_i - 1$で置き換え、排他的論理和での総和を$0$にできるような$k$の最小値が答え。 $a_i \mapsto a_i - 1$と置き換えたときの差分$a_i \oplus (a_i - 1)$は常に$2^b - 1$の形をしている。 総和$\sum a_i$から差分$a_i \oplus (a_i - 1)$を$b$の大きい順に引いていって$0$にしようとすればよい。 $O(N + \log a_{\mathrm{max}})$。

implementation

#include <cstdio>
#include <vector>
#define repeat(i, n) for (int i = 0; (i) < int(n); ++(i))
#define repeat_reverse(i, n) for (int i = (n)-1; (i) >= 0; --(i))
using namespace std;

int main() {
    // input
    int n; scanf("%d", &n);
    vector<int> a(n); repeat (i, n) scanf("%d", &a[i]);
    // solve
    int sum_a = 0;
    vector<bool> cnt(30);
    for (int a_i : a) {
        sum_a ^= a_i;
        cnt[__builtin_ctz(a_i)] = true;
    }
    int result = 0;
    repeat_reverse (i, 30) {
        if (sum_a & (1 << i)) {
            if (not cnt[i]) {
                result = -1;
                break;
            } else {
                result += 1;
                sum_a ^= (1 << (i + 1)) - 1;
            }
        }
    }
    // output
    printf("%d\n", result);
    return 0;
}