WARush

SRMの結果とか、解けた問題のコードを書いていきます

SRM699 Div1 Easy "OthersXor"

解法

N匹の狼がいて、何匹か飴を1つ持っている。ここで、それぞれの狼に「あなたを除く他の狼たちの飴を合計するとそれは奇数?偶数?」と聞く。狼たちは正直に答えるが、黙秘権を行使する狼もいる。この時、飴の合計の最小はいくつだ?

というような問題に置き換えられる。

「奇数です」と答えた狼がいなかったとすると、全員の狼が飴を持っていない場合で整合性がとれるため、飴の数は0でよい。

では、「奇数です」と答えた狼がいた場合はどうか?
まず、「奇数です」と答えた狼同士、もしくは「偶数です」と答えた狼同士、それら全員飴を持っているか、誰も持っていないかでなければならない。なぜなら、持っている狼と持っていない狼の合計値の差が1になってしまうため、偶奇がズレてしまうからである。なので、「奇数です」と答えた狼全員に持たせるか、「偶数です」と答えた狼全員に持たせるか、小さいほうをとればよい。

事後

難しいけど偶奇を考えるのは面白いなぁ

ソースコード

class OthersXor {
    public:
    long long minSum(vector<int> x) {
        int n = x.size();
        long long ans = 0;
        // それぞれのビット位置で回す
        for (int mask = 1; mask < 1 << 30; mask <<= 1) {
            // 奇数・偶数・黙秘の数を数え上げる
            int odd = 0;
            int even = 0;
            int unknown = 0;
            for (int i = 0; i < n; i++) {
                if (x[i] == -1) {
                    unknown++;
                } else {
                    if ((x[i] & mask) != 0) {
                        odd++;
                    } else {
                        even++;
                    }
                }
            }
            if (odd != 0) {
                // 奇数と答えた狼がいた場合
                const int INF = 50;
                int use_odd = INF;
                // 奇数組全員に持たせる場合
                if (odd % 2 == 0) {
                    use_odd = odd;
                } else if (unknown != 0) {
                    use_odd = odd + 1;
                }
                // 偶数組全員に持たせる場合
                int use_even = INF;
                if (even % 2 != 0) {
                    use_even = even;
                } else if (unknown != 0) {
                    use_even = even + 1;
                }
                // 奇数偶数小さいほうをとる
                int use_num = min(use_odd, use_even);
                if (use_num == INF) {
                    return -1;
                }
                ans += (long long)mask * use_num;
            }
        }
        return ans;
    }
};