2014 Yandex.Algorithm Elimination Stage, Round1 D - Splitting Money
問題概要
n 個の数列が与えられる.
各要素について,集合Aとして使うか,集合Bとして使うか,あるいは使わないかを選択できる.
最終的に,集合Aとして使った数字のXORと,集合Bとして使った数字のXORが等しくなるようにしたい.
そのような集合AとBは何通りあるか.ただし,同じ数字であっても,元の数列で違う位置にあるものであれば,それらは区別するものとする.
・制約
1 <= n <= 10^4
1 <= a_i <= 10^4
解法
まず,数字の値が小さいことに注目する.
また,集合AのXORと集合BのXORが等しいとは,そもそもどちらかの集合に含めるとしたすべての要素のXORが0であることと同値である.
そのように数字を選んだ場合,各要素をどちらに振り分けても,それぞれのXORの値は等しくなる.
以上の2つから,以下の dp で計算することができる.
dp[i][j] := i 番目の要素まで見て,どちらかの集合として使う要素の XOR の値が j となるような値の振り分け方
ある数字を集合として使う時,どちらの集合に入れるかで2通りあるので,遷移は
dp[i + 1][j ^ a[i]] += dp[i][j] * 2
となる.
計算量は O(n * a_i) .
ソースコード
#include <bits/stdc++.h> using namespace std; constexpr int mod = 1e9 + 7; int main() { int n; cin >> n; vector<int> a(n); for(int i = 0; i < n; ++i) { cin >> a[i]; } vector<int> dp(1 << 14); dp[0] = 1; for(int i = 0; i < n; ++i) { auto nxt = dp; for(int j = 0; j < (int)dp.size(); ++j) { const int x = j ^ a[i]; if(x < (int)nxt.size()) { (nxt[x] += (2 * dp[j]) % mod) %= mod; } } dp = move(nxt); } cout << dp[0] << endl; }