https://apc001.contest.atcoder.jp/tasks/apc001_f
最近同じような問題を解いたのでわかったんですけども、O(3^N)DPをバグらせて悲しくなりました…。
頂点に隣接しているすべての辺のXORを書き込みます。すると、2つの頂点についてある値XでのXORを行い、すべての頂点を0にする問題になりました。そうすれば、頂点の値がiのものの数をciとして、i=0は考えなくて良くて、ci(i>=1)は[ci/2]個のペアを作るのが最適です。あと余ったものでO(3^N)DPをすればいいです。
XORを区間に施す際の一般的なテクニックについてですが、2項の差というよりも、2つの値のXORと考えたほうが良さそうですね。
bitとbit2を混同してWA出したのは情けない…。for(int j = (i - 1) & i; j ; j = (j -1)&i))みたいな書き方を速くマスターしましょう…。
これで1000点埋め完了です。
(3/4更新)
rep(bit, 0, (1 << N)) { for(int to = bit; to; to = (to - 1) & bit) { MAX(dp[bit], dp[to ^ bit] + ok[to]); } } }
このようにしてもできます。vector使わず簡潔でいいですね。
bit^toがbitの部分集合(bitを含まない)となります。
int A[MAX_N]; int cnt[20]; int dp[(1 << 16)]; int ok[(1 << 16)]; vector<int> vec; void solve() { cin >> N; rep(i, 0, N - 1) { int a, b, c; cin >> a >> b >> c; A[a] ^= c; A[b] ^= c; } rep(i, 0, N) cnt[A[i]]++; int ans = 0; rep(i, 1, 16) { ans += cnt[i] / 2; if(cnt[i] % 2) vec.pb(i); } M = sz(vec); rep(bit, 1, (1 << M)) { int tmp = 0, res = 0; rep(i, 0, M) { if(!(bit & (1 << i))) continue; tmp ^= vec[i]; res++; } if(tmp == 0) ok[bit] = res - 1; else ok[bit] = M; } fill(dp, dp + (1 << M), M); dp[0] = 0; rep(bit, 0, (1 << M)) { vector<int> vt; rep(i, 0, M) { if(!(bit & (1 << i))) vt.pb(i); } rep(bit2, 1, (1 << sz(vt))) { int to = 0; rep(i, 0, sz(vt)) { if(bit2 & (1 << i)) to |= (1 << vt[i]); } MIN(dp[bit | to], dp[bit] + ok[to]); } } cout << ans + dp[(1 << M) - 1] << "\n"; }