ARC146 C - Even XOR (600, 橙)
お気持ち
- 本番は、「奇数個OKの条件がなければ/0が必ずSに含まれる場合なら」簡単なのになぁと思いながら複雑に考えてしまい、頭が爆発しました。
- 最初に思いつきやすいのは、以下の性質かなと思います。そして、この辺の考えがグルグル回ってしまい、収集がつかなくなるのも日常茶飯事。
- (1) 集合Sが独立なら部分集合のXORは相異なる
- (2) 集合Sが独立という条件なら数え上げが簡単
- (3) 集合Sに必ず0を入れるパターンはS/{0}が独立ならOK
- あながち悪い方針ではないですが、(2)(3)は特殊ケースの性質になっていて、(1)や(1)の証明に使われる性質を上手く活かせていない気がします。もう少し基本的な性質を見ていきたいですね。
考察
基本的な操作に対して、いい性質を見つけていく感じで解けました。
2枚目のメモにWAと書いてありますが、解法は正しいです。
最初、サンプル1の答えが11
になったので首をかしげていましたが、 の逆元を計算していなかっただけでした。
ソースコード
#include <iostream> #include <atcoder/modint> #define rep(i, n) for(i = 0; i < n; i++) using namespace std; using namespace atcoder; using mint = modint998244353; int n; mint dp[200002][2]; mint pw2[200002]; mint inv[200002], fact[200002], finv[200002]; //(n+1)!の逆数まで使うので注意 void init(int n) { int i; pw2[0] = 1; rep(i, n) pw2[i + 1] = pw2[i] * 2; inv[1] = fact[1] = fact[0] = finv[1] = finv[0] = 1; int mod = 998244353; for (i = 2; i <= n; i++) { fact[i] = i * fact[i - 1]; inv[i] = -inv[mod % i] * (mod / i); finv[i] = inv[i] * finv[i - 1]; } } int main() { int i, j; cin >> n; init(n + 1); dp[0][0] = 1; dp[0][1] = 0; dp[1][0] = pw2[n] - 1; dp[1][1] = 1; for (i = 1; i <= n; i++) { dp[i + 1][0] = dp[i][0] * (pw2[n] - pw2[i]); dp[i + 1][1] = dp[i][0] * pw2[i - 1] + dp[i][1] * (pw2[n] - pw2[i - 1]); } mint ans = 0; rep(i, n + 2) { ans += (dp[i][0] + dp[i][1]) * finv[i]; } cout << ans.val() << endl; return 0; }