【ABC365】AtCoder Beginner Contest 365【C++】

### コンテスト名
トヨタ自動車プログラミングコンテスト2024#8（AtCoder Beginner Contest 365）

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

### 開催日
2024/08/03 21:00-22:40

---

## [A: Leap Year](https://atcoder.jp/contests/abc365/tasks/abc365_a)
**解法**
- 問題文通りに判定する
```cpp:ABC365A.cpp
#include <iostream>
using namespace std;

int main(){
    int y;
    cin >> y;

    if(y%4!=0){
        cout << 365 << endl;
    }else if(y%100!=0){
        cout << 366 << endl;
    }else if(y%400!=0){
        cout << 365 << endl;
    }else{
        cout << 366 << endl;
    }

    return 0;
}
```

## [B: Second Best](https://atcoder.jp/contests/abc365/tasks/abc365_b)
**解法**
- `vector<pair<int, int>>` で要素の値と位置を記録する
- 降順にソートして 2 番目に大きい要素を取得する
```cpp:ABC365B.cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

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

    vector<pair<int, int>> A;
    int a;
    for(int i=0; i<n; i++){
        cin >> a;
        A.emplace_back(a, i+1);
    }

    sort(A.rbegin(), A.rend());
    auto [value, num] = A[1];
    cout << num << endl;
    return 0;
}
```

## [C: Transportation Expenses](https://atcoder.jp/contests/abc365/tasks/abc365_c)
**解法**
1. 累積和を利用して仮の上限額を求める
    - 交通費の配列 $A$ を昇順にソートして累積和を求める
    - 仮の上限額 $x'$ を $A_i$ のなかから選ぶ
    - 交通費が仮の上限額 $x'$ 以下の人 $i$ には $A_i$ 円が支給されるため、累積和を利用して合計を求める
    - 交通費が仮の上限額 $x'$ より大きい人には $x'$ 円が支給されるため、（交通費が仮の上限額 $x'$ より大きい人の人数 $\times \ x'$ ）を求める
    - $M$ 円からこれらの総和を引いた残りの分を交通費が仮の上限額 $x'$ より大きい人に分配する
    - $A$ の最大値を $x$ にしたときでも交通費補助額の総和が $M$ 円以下になるならば、答えは無限になる
    - $A$ の最小値を仮の上限額 $x'$ にしたときでも条件を満たせない場合は、 $\lfloor \frac{M}{N} \rfloor$ が答えである
    - 計算量は $O(N \log N)$
2. 答えを二分探索で求める
    - 交通費の配列 $A$ を昇順にソートして答えを二分探索で求める
    - 計算量は $O(N \log (\text{max}A))$
```cpp:ABC365C_1.cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

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

    vector<long long int> A(n), S(n);
    for(int i=0; i<n; i++){
        cin >> A[i];
        S[i] = A[i];
    }

    sort(A.begin(), A.end());
    sort(S.begin(), S.end());

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

    for(int i=n-1; i>=0; i--){
        long long int sum = S[i];
        sum += A[i] * (n - 1 - i);

        if(sum<=m){
            if(A[i]==A[n-1]){
                cout << "infinite" << endl;
            }else{
                cout << A[i] + (m - sum)/(n - 1 - i) << endl;
            }
            return 0;
        }
    }

    cout << m / n << endl;
    return 0;
}
```
```cpp:ABC365C_2.cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

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

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

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

    int low = 0, high = A.back() + 1;
    while(high-low>1){
        int mid = low + (high - low)/2;

        long long int sum = 0;
        for(int i=0; i<n; i++){
            sum += min(mid, A[i]);
        }

        if(sum<=m){
            low = mid;
        }else{
            high = mid;
        }
    }

    if(low>=A.back()){
        cout << "infinite" << endl;
    }else{
        cout << low << endl;
    }

    return 0;
}
```

## [D: AtCoder Janken 3](https://atcoder.jp/contests/abc365/tasks/abc365_d)
**解法**
- 動的計画法 (DP)
- $\text{dp}[i][j]$ := $i$ 回目に手 $j$ を出すときの勝った回数の最大値
- $\text{R} = 0, \text{P} = 1, \text{S} = 2$ とすると、 $i - 1$ 回目の結果から求められる
    - 相手が $i$ 回目に $\text{R} = 0$ を出したとき
        - あいこ： $\text{dp}[i][0] = \text{max}(\text{dp}[i-1][1], \text{dp}[i-1][2])$
        - 勝ち： $\text{dp}[i][1] = \text{max}(\text{dp}[i-1][0], \text{dp}[i-1][2]) + 1$
```cpp:ABC365D.cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

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

    string s;
    cin >> s;

    vector<vector<int>> dp(n+1, vector<int>(3));
    for(int i=0; i<n; i++){
        if(s[i]=='R'){
            dp[i+1][0] = max(dp[i][1], dp[i][2]);
            dp[i+1][1] = max(dp[i][0], dp[i][2]) + 1;
        }else if(s[i]=='P'){
            dp[i+1][1] = max(dp[i][0], dp[i][2]);
            dp[i+1][2] = max(dp[i][0], dp[i][1]) + 1;
        }else if(s[i]=='S'){
            dp[i+1][2] = max(dp[i][0], dp[i][1]);
            dp[i+1][0] = max(dp[i][1], dp[i][2]) + 1;
        }
    }

    int ans = 0;
    for(int i=0; i<3; i++){
        ans = max(ans, dp[n][i]);
    }

    cout << ans << endl;
    return 0;
}
```