バイトの競プロメモ

主に競技プログラミング

C - ゲーマーじゃんけん  dwangoプログラミングコンテスト

C: ゲーマーじゃんけん - dwangoプログラミングコンテスト | AtCoder

N人の中からK人が勝つのにかかる回数の期待値が分かれば解ける。
N人の内、k人が勝つ確率をp(N,K)、N人のじゃんけんが終わる回数の期待値をENとして
{ \displaystyle
E_N = \sum_{K=1}^{N} p(N,K) * (E_K + 1)
}
整理してやって
{ \displaystyle
E_N = \frac{1+\sum_{K=1}^{N - 1} p(N,K)  (E_K)}{1 - P(N,N)}
}
P(N,K)を求めるには、N人の手の種類全部につき、それらが何通りあるかを調べ、
その手で勝つ人数(引き分けならN人が勝つ)をiとして、P(N,i)に加算してやり、最後に全体の通りで割ればいい。
それぞれの手の個数がgcpの時の通り
{ \displaystyle
\frac{(g+c+p)!}{g!c!p!}
}
Submission #3884323 - dwangoプログラミングコンテスト | AtCoder

vd fact(128);
vd dp(128); //  i人をさばく時の期待値回数
double comb(int g, int c, int p) {
    return fact[g + c + p] / fact[g] / fact[c] / fact[p];
}
double dfs(int x) {
    if (dp[x] >= 0) return dp[x];
    //gcpの数について、それぞれ何通りあるかしらべ
    //j人が勝つなら、tori[j] += する
    vd per(128); // i人が勝つ時の通り
    rep(g, x + 1) {
        rep(c, x + 1 - g) {
            int p = x - g - c;
            vi te;
            if (g)te.pb(g);
            if (c)te.pb(c);
            if (p)te.pb(p);
            sort(te);
            int rare = te[0];
            switch (count(all(te), rare)) {
                case 1:
                    per[rare] += comb(g, c, p);
                    break;
                case 2:
                    per[rare] += comb(g, c, p);
                    break;
                case 3:
                    per[x] += comb(g, c, p);
                    break;
            }
        }
    }
    double zen = sum(per);
    for (auto &&a :per)a /= zen;
    double res = 1;
    rep(i, 1, x) res += per[i] * dfs(i);
    res /= 1 - per[x];
    return dp[x] = res;
}
signed main() {
    cin >> N;
    fact[0] = 1;
    rep(i, 1, 128)fact[i] = fact[i - 1] * i;
    fill(dp, -1);
    dp[1] = 0;
    cout << dfs(N) << endl;
    return 0;
}