---
# System prepended metadata

title: ABC314 E - Roulettes

---

# ABC314 E - Roulettes

解法の正当性がやや不安です.

---

## 問題リンク
- https://atcoder.jp/contests/abc314/tasks/abc314_e

## 解法
数学的な計算のみでは答えは求まりそうないので, 期待値DPでうまくやります.

### DP
- $f(x):=$高橋君が最適な戦略をとったとき,スコアが$x$以上になるまでに必要なコストの期待値

とします.この時,以下が成り立ちます.

$$f(x)=\min_{i=1,2,\dots,N}{E_i}$$

ただし,
$$E_i=\frac{1}{P_i}{\sum_{j=1}^{P_i}}\left(C_i+f(x-S_{i,j})\right)$$

で $E_i$ は $i$ 番目のルーレットを選んだ時を選んだ時にかかるコストの期待値を表します.

$f$ の初期条件として,以下が成り立ちます.

- $x\leq 0$ に対して $f(x)=0$

あとはこの通りにメモ化再帰するとうまくいきそうです...

```cpp=
using ld = long double;
vector<ld> dp(m + 1, -1);  //
auto f = [&](const auto& f, int x) -> ld {
    if (x <= 0) return 0;
    if (dp[x] >= 0) return dp[x];
    dp[x] = infl;

    rep(i, n) {
        ld e = 0;                                                 // 期待値
        rep(j, p[i]) e += (ld)(c[i] + f(f, x - s[i][j])) / p[i];  // 遷移

        chmin(dp[x], e);
    }
    return dp[x];
};
cout << fixed << setprecision(12) << f(f, m) << '\n';
```

が実はそうでもありません.

### 問題点

これだとサンプル2には通りません.$S_{i,j}=0$ の時に壊れます.

$S_{i,j}=0$ の時,上の式のままだと$f(x)$ を求めるために$f(x)$の値を必要としますが,これはおかしいですね...

### 解消
こういう時,遷移の式をうまく整理(移項)することで解消できたりします.

今回もそれが使えます...が少しややこしいです.


$f(x)$ を求める際の遷移の式に注目します.

$f(x)=\min E$ なので $E_i$ が最小となるような $i$ をとります.この時,もちろん $f(x)=E_i$です.

$S_{i,j}=0$ であるような $j$ の個数を $z_i$ とすることで $E_i(=f(x))$ は次のように書けます.

$$E_i=f(x)=C_i+\frac{1}{P_i}\left(z_if(x)+\sum_{S_{i,j}\neq0}{f(x-S_{i,j})}\right)$$

したがって移項すると

$$\left(1-\frac{z_i}{P_i}\right)f(x)=C_i+\frac{1}{P_i}\sum_{S_{i,j}\neq0}f(x-S_{i,j})$$

となり,

$$f(x)=\frac{1}{P_i-z_i}\left(P_iC_i+\sum_{S_{i,j}\neq0}f(x-S_{i,j})\right)\cdots\bigstar$$

が得られます.

したがって, $E_i=f(x)$ となるような $i$ に対して $\bigstar$ が成り立ち,遷移が行えます.

実際に条件を満たすような $i$ を特定するのは無理そうですが,$i=1,2,\dots,N$ に対して $E_i=f(x)$ と仮定して $\bigstar$ により $f(x)$ を求めることにします.そうして求めた $f(x)$ うち,最小のものが正しい $f(x)$ です.


以上より計算量 $O(M\sum{P_i})$ で答えである $f(M)$ 求めることができます.

### 実装
```cpp=
int main() {
    int n, m;
    read(n, m);
    vector<int> c(n), p(n);
    vector<vector<int>> s(n);
    rep(i, n) {
        read(c[i], p[i]);
        rep(j, p[i]) {
            int a;
            read(a);
            s[i].emplace_back(a);
        }
    }
 
    vector<ld> dp(m + 1, -1);
    auto f = [&](const auto& f, int x) -> ld {
        if (x <= 0) return 0;
        if (dp[x] >= 0) return dp[x];
        dp[x] = infl;
 
        rep(i, n) {
            int zero = 0;
            ld sum = (ld)p[i] * c[i];
            rep(j, p[i]) {
                if (s[i][j]) {
                    sum += f(f, x - s[i][j]);
                } else {
                    ++zero;
                }
            }
            ld e = (ld)sum / (p[i] - zero);
            chmin(dp[x], e);
        }
        return dp[x];
    };
    cout << fixed << setprecision(12) << f(f, m) << '\n';
}
```
- https://atcoder.jp/contests/abc314/submissions/44553622