【ABC382】AtCoder Beginner Contest 382【C++】

### コンテスト名
AtCoder プログラミングコンテスト2024（AtCoder Beginner Contest 382）

### コンテストURL
https://atcoder.jp/contests/abc382

### 開催日
2024/11/30 21:00-22:40

---

## [A: Daily Cookie](https://atcoder.jp/contests/abc382/tasks/abc382_a)
**解法**
- もとから空き箱の個数と $D$ を足す
```cpp:ABC382A.cpp
#include <iostream>
#include <string>
using namespace std;

int main(){
    int n, d;
    cin >> n >> d;

    string s;
    cin >> s;

    int cnt = 0;
    for(int i=0; i<n; i++){
        if(s[i]=='.'){
            cnt++;
        }
    }

    cout << cnt + d << endl;

    return 0;
}
```

## [B: Daily Cookie 2](https://atcoder.jp/contests/abc382/tasks/abc382_b)
**解法**
- 問題文通りにシミュレーションする
- 文字列 $S$ を右から順にたどり、 $D$ 個の `@` を `.` に変換する
```cpp:ABC382B.cpp
#include <iostream>
#include <string>
using namespace std;

int main(){
    int n, d;
    cin >> n >> d;

    string s;
    cin >> s;

    int cnt = 0;
    for(int i=n-1; i>=0; i--){
        if(s[i]=='@'){
            cnt++;
            s[i] = '.';
        }
        if(cnt==d){
            break;
        }
    }

    cout << s << endl;

    return 0;
}
```

## [C: Kaiten Sushi](https://atcoder.jp/contests/abc382/tasks/abc382_c)
**解法**
- 二分探索
1. 寿司をソートする方法
    - 寿司を美味しさの昇順にソートし、各人が仮に先頭にいた場合に、どの寿司を食べるかを記録する
    - 各人の並び順を考慮し、答えを求める
2. 人をソートする方法
    - 自分より美食度が低い人が前にいる人は、寿司を食べられない
    - 美食度が単調減少になるように、寿司を食べられない人を無視する
    - `lower_bound(greater<int>())` で美食度が初めて各寿司の美味しさ $B$ 以下になる人を探索する
```cpp:ABC382C_1.cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(){
    int n, m;
    cin >> n >> m;

    vector<int> A(n);
    vector<pair<int, int>> B;
    int b;
    for(int i=0; i<n; i++){
        cin >> A[i];
    }
    for(int i=0; i<m; i++){
        cin >> b;
        B.emplace_back(b, i);
    }

    sort(B.begin(), B.end());

    vector<int> V(n);
    for(int i=0; i<n; i++){
        int idx = lower_bound(B.begin(), B.end(), make_pair(A[i], 0)) - B.begin();
        V[i] = idx;
    }

    vector<int> ans(m, -1);
    int now = m - 1;
    for(int i=0; i<n; i++){
        if(V[i]>now){
            continue;
        }
        for(int j=V[i]; j<=now; j++){
            auto [bvalue, bidx] = B[j];
            ans[bidx] = i + 1;
        }
        now = V[i] - 1;
    }

    for(int i=0; i<m; i++){
        cout << ans[i] << '\n';
    }

    return 0;
}
```
```cpp:ABC382C_2.cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main(){
    int n, m;
    cin >> n >> m;

    vector<int> A(n);
    vector<int> B(m);
    for(int i=0; i<n; i++){
        cin >> A[i];
    }
    for(int i=0; i<m; i++){
        cin >> B[i];
    }

    for(int i=1; i<n; i++){
        A[i] = min(A[i], A[i-1]);
    }

    vector<int> ans(m, -1);
    for(int i=0; i<m; i++){
        int idx = lower_bound(A.begin(), A.end(), B[i], greater<int>()) - A.begin();
        if(idx<n){
            ans[i] = idx + 1;
        }
    }

    for(int i=0; i<m; i++){
        cout << ans[i] << '\n';
    }

    return 0;
}
```

## [D: Keep Distance](https://atcoder.jp/contests/abc382/tasks/abc382_d)
**解法**
- 再帰関数
- $A_N \leqq M$ を満たせないものは枝刈りする
```cpp:ABC382D.cpp
#include <iostream>
#include <vector>
using namespace std;

int n, m;
vector<vector<int>> ans;

void f(int cnt, vector<int> &A, int last){
    if(cnt==n){
        ans.push_back(A);
        return;
    }

    for(int i=last+10; i<=m-10*(n-cnt-1); i++){
        A.push_back(i);
        f(cnt+1, A, i);
        A.pop_back();
    }
}

int main(){
    cin >> n >> m;

    vector<int> first;
    f(0, first, -9);

    cout << ans.size() << endl;
    for(int i=0; i<ans.size(); i++){
        for(int j=0; j<n; j++){
            if(j){
                cout << " ";
            }
            cout << ans[i][j];
        }
        cout << '\n';
    }

    return 0;
}
```

## [E: Expansion Packs](https://atcoder.jp/contests/abc382/tasks/abc382_e)
**解法**
- 期待値動的計画法 (DP)
- 1 つのパックに入っているレアカードの枚数の確率分布を求める
    - $\text{dp1}[i][j]$ := $i$ 枚目までで $j$ 枚レアカードが出る確率
    - $\text{dp1}[i+1][j+1]$ += $\text{dp1}[i][j] \times \frac{\text{P}[i]}{100}$
    - $\text{dp1}[i+1][j]$ += $\text{dp1}[i][j] \times (1 - \frac{\text{P}[i]}{100})$
- 開封するパックの個数の期待値を求める
    - $\text{dp2}[i]$ := レアカードが $X$ 枚から残り $i$ 枚の状態で、 $X$ 枚になるまでのパックの個数の期待値
    - $\text{dp2}[i] = 1 + \sum\limits_{j=0}^N \text{dp2}[\max (i-j, 0)] \times \text{dp1}[N][j]$
    - 両辺にある $\text{dp2}[i]$ を移項して、 $\text{dp2}[i] = \frac{1 + \sum\limits_{j=1}^N \text{dp2}[\max (i-j, 0)] \times \text{dp1}[N][j]}{1 - \text{dp1}[N][0]}$
```cpp:ABC382E.cpp
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

int main(){
    int n, x;
    cin >> n >> x;

    vector<int> P(n);
    for(int i=0; i<n; i++){
        cin >> P[i];
    }

    vector<vector<double>> dp1(n+1, vector<double>(n+1));
    dp1[0][0] = 1;
    for(int i=0; i<n; i++){
        for(int j=0; j<i+1; j++){
            dp1[i+1][j+1] += dp1[i][j] * P[i]/100.0;
            dp1[i+1][j] += dp1[i][j] * (1 - P[i]/100.0);
        }
    }

    vector<double> dp2(x+1);
    for(int i=1; i<=x; i++){
        dp2[i] += 1;
        for(int j=1; j<=n; j++){
            dp2[i] += dp2[max(i-j, 0)] * dp1[n][j];
        }
        dp2[i] /= 1 - dp1[n][0];
    }

    printf("%.10f\n", dp2[x]);

    return 0;
}
```