AtCoder ABC315 C - Flavors: 解き方あれこれ(C, Python)

https://atcoder.jp/contests/abc315/tasks/abc315_c

良く分からない解き方(sparse table)をしたついでに思いついた解法と実装と実装練習。

## 解説解法

- 各味毎で一番おいしい数値を取り出し、それらをソートしてパターン1の最大のおいしさを得ます
- 各味内で降順ソートして[0] + [1]//2で最大のおいしさを得ます

時間計算量はソートがネックになり$O(NlogN)$です。

<details><summary>実装(Python)</summary><div>

```Python
from collections import defaultdict
ans = -1
n = int(input())
taste = defaultdict(list) # taste[i] = 味iのおいしさリスト
for _ in range(n):
    f, s = map(int, input().split())
    taste[f].append(s)
maxs = []
for key in taste.keys():
    taste[key].sort(reverse=True) # 各味をソート
    maxs.append(taste[key][0]) # 各味の最大のおいしさを入れる
# パターン1:異なる味
maxs.sort(reverse=True)
if len(maxs) >= 2: ans = max(ans, maxs[0] + maxs[1])
# パターン2:同じ味
for key in taste.keys():
    if len(taste[key]) < 2: continue # 1つしかないならNG
    ans = max(ans, taste[key][0] + taste[key][1] // 2)
print(ans)
```

</div></details>

https://atcoder.jp/contests/abc315/submissions/44823686

# ソートしてi-1,iを比較
$(f,s)$で読み込んだリストをソートすると味毎にかたまり、各味は昇順に並びます。そこで、`今と違う味の最大のおいしさ`を覚えておきます。また、`今の味の最大のおいしさ`を覚えておきます。

- 味変した場合、`今と違う味の最大のおいしさ`を更新します。
- 味変していない場合、1つ前の要素と比較すれば同じ味の式です。各味の最後で、`f[i-1]//2 + f[i]`はその味の最大のおいしさの組み合わせになります。
- いずれにしても、今のおいしさ + `今と違う味の最大のおいしさ`は最大のおいしさになる候補です。

時間計算量はソートがネックになり$O(NlogN)$です。

<details><summary>実装(Python)</summary><div>

```Python
ans = -1
n = int(input())
dat = []
for _ in range(n): dat.append(list(map(int, input().split()))) # [f,s]の配列
dat.sort() # 各味毎に固まり、各味内は昇順のおいしさ
lastF = lastS = 0
maxSOtherF = 0 # そのほかの味で最大のおいしさ
maxSCurrentF = 0 # 今の味で最大のおいしさ
for f, s in dat:
    if lastF != f: # 味が変化した
        maxSOtherF = max(maxSCurrentF, maxSOtherF)
        maxSCurrentF = s # "今の味の最大のおいしさ"をreset
    else: # 味が変化していない場合
        ans = max(ans, lastS // 2 + s) # 同じ味を取る
    ans = max(ans, s + maxSOtherF) # 違う味
    lastF, lastS = f, s
    maxSCurrentF = max(maxSCurrentF, s)
print(ans)
```
</div></details>

https://atcoder.jp/contests/abc315/submissions/44823881

# 味の数を数えておき、ソートして区間max(segtree)
先にソートをして味毎に連続した状態にします。ある味の長さとある味の開始位置が分かれば、その味の範囲及び、その味以外の範囲が分かります。

区間maxが取れるデータ構造で区間maxクエリを行えば$O(NlogN)$です。

<details><summary>実装(C++)</summary><div>

```C++
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace std;
using namespace atcoder;
using ll = long long int;
#define FOR(i, begin, end) for(int i=(begin),i##_end_=(end);i<i##_end_;i++)
#define REP(i, n) FOR(i,0,n)
pair<int, int> op(pair<int, int> a, pair<int, int> b) { return pair<int, int>{-1, max(a.second, b.second)}; }
pair<int, int> e() { return pair<int, int>{0,0}; }
int main() {
    int ans = -1;
    int n; cin >> n;
    vector<pair<int, int>> dat;
    map<int, int> tasteLen;
    REP(i,n){
        int f, s; cin >> f >> s;
        ++tasteLen[f];
        dat.push_back(pair<int, int>{f, s});
    }
    sort(dat.begin(), dat.end());
    segtree<pair<int, int>, op, e> seg(dat);
    int lastTaste = -1;
    int thisTasteFirst = -1;
    REP(i, n){
        auto &elem = dat.at(i);
        if(elem.first != lastTaste){
            thisTasteFirst = i;
            lastTaste = elem.first;
        }
        // 違う味とのmax
        ans = max(ans, elem.second + seg.prod(0, thisTasteFirst).second);
        ans = max(ans, elem.second + seg.prod(thisTasteFirst + tasteLen.at(elem.first), n).second);
        // 同じ味
        ans = max(ans, elem.second/2 + seg.prod(thisTasteFirst, i).second);
        ans = max(ans, elem.second/2 + seg.prod(i+1, thisTasteFirst + tasteLen.at(elem.first)).second);
    }
    cout << ans << endl;

}

```

</div></details>

https://atcoder.jp/contests/abc315/submissions/44824503

# 味の数を数えておき、ソートして区間max(sparse table)

区間maxをsprase tableで各クエリを$O(1)$とします。が、sortが必要なので$O(NlogN)$で変わりません。

<details><summary>実装(c++)</summary><div>

```c++
#include <bits/stdc++.h>
using namespace std;
using ll = long long int;
#define FOR(i, begin, end) for(int i=(begin),i##_end_=(end);i<i##_end_;i++)
#define REP(i, n) FOR(i,0,n)

template<typename T>
class SparseTable {
    vector<vector<T>> table;
    vector<int> lookup;
    function<T(T, T)> operation;
    function<T()> empty;
public:
    SparseTable(const vector<T> &v, function<T(T, T)> ope, function<T()> e): operation(ope), empty(e) {
        int log_len = 0;
        while((1 << log_len) <= (int)v.size()) ++log_len;
        table = vector<vector<T>>(log_len, vector<T>(1 << log_len));
        lookup = vector<int>((int)v.size() + 1);
        for(int i = 0; i < (int)v.size(); i++) table[0][i] = v[i];
        for(int i = 1; i < log_len; i++) {
            for(int j = 0; j + (1 << i) <= (1 << log_len); j++) {
                table[i][j] = operation(table[i-1][j], table[i - 1][j + (1 << (i - 1))]);
            }
        }
        for(int i = 2; i < (int)lookup.size(); i++) lookup[i] = lookup[i >> 1] + 1;
    }

    inline T queryCloseOpen(const int l, const int r) {
        assert(r-l >= 0);
        if(r-l == 0) return empty();
        int b = lookup[r - l];
        return operation(table[b][l], table[b][r - (1 << b)]);
    }
};

int main() {
    auto op = [] (pair<int, int> a, pair<int, int> b) {return pair<int, int>{-1, max(a.second,b.second)};};
    auto e = [](){return pair<int, int>{0,0};};
    int ans = -1;
    int n; cin >> n;
    vector<pair<int, int>> dat;
    map<int, int> tasteLen;
    REP(i,n){
        int f, s; cin >> f >> s;
        ++tasteLen[f];
        dat.push_back(pair<int, int>{f, s});
    }
    sort(dat.begin(), dat.end());
    SparseTable<pair<int, int>> seg(dat, op, e);
    int lastTaste = -1;
    int thisTasteFirst = -1;
    REP(i, n){
        auto &elem = dat.at(i);
        if(elem.first != lastTaste){
            thisTasteFirst = i;
            lastTaste = elem.first;
        }
        // 違う味とのmax
        ans = max(ans, elem.second + seg.queryCloseOpen(0, thisTasteFirst).second);
        ans = max(ans, elem.second + seg.queryCloseOpen(thisTasteFirst + tasteLen.at(elem.first), n).second);
        // 同じ味
        ans = max(ans, elem.second/2 + seg.queryCloseOpen(thisTasteFirst, i).second);
        ans = max(ans, elem.second/2 + seg.queryCloseOpen(i+1, thisTasteFirst + tasteLen.at(elem.first)).second);
    }
    cout << ans << endl;

}
```

</div></details>

https://atcoder.jp/contests/abc315/submissions/44826878