A - 料理コンテストと食材 / Cooking Contest and Ingredients 解説 by admin
GPT 5.2 High概要
得点順(同点なら番号が小さい順)で上位 \(K\) 人のシェフを選び、その \(K\) 人全員が共通して得意な食材の種類数(集合の共通部分の大きさ)を求めます。
考察
まず「決勝進出者」は、得点 \(V_i\) の降順、同点の場合はシェフ番号の昇順で並べたときの先頭 \(K\) 人で一意に決まります。よって最初にやるべきことは 上位 \(K\) 人の特定 です。
次に求めたいのは、決勝進出した \(K\) 人の集合 \(S_{i_1}, S_{i_2}, \dots, S_{i_K}\) の共通部分 [ |S_{i1} \cap S{i2} \cap \cdots \cap S{i_K}| ] です。
素朴に「各食材 \(t\) について、上位 \(K\) 人全員が持つかを調べる」と、各食材ごとに \(K\) 人を確認して \(O(MK)\) になり、最大で \(10^5 \times 10^5\) となって間に合いません。
ここで重要な観察は、入力全体の「得意食材の総数」が [ \sum C_i \le 2\times 10^5 ] と小さいことです。つまり、食材情報は疎(まばら)なので、「現れた食材だけを数える」方向にすると高速に処理できます。
具体的には、上位 \(K\) 人に含まれる各食材 \(t\) の出現回数を数え、回数がちょうど \(K\) なら「全員が持っている」と判断できます。
アルゴリズム
- 各シェフ \(i\) について、得点 \(V_i\) と得意食材リスト \(S_i\) を読み込む。
- シェフをキー
(-V[i], i)(得点降順・番号昇順)でソートし、先頭 \(K\) 人を決勝進出者として取り出す。 - 配列
cnt[t]を用意し、決勝進出者の各食材 \(t\) についてcnt[t] += 1として出現回数を数える。 - \(t=1\) から \(M\) まで走査し、
cnt[t] == Kを満たす食材の個数を答えとして出力する。
(例) - 決勝進出者が \(K=3\) 人で、食材 1 が 3 回出現していれば、3 人全員が食材 1 を得意 ⇒ 使える食材にカウント。
計算量
- 時間計算量: ソートが \(O(N\log N)\)、カウントが \(O\!\left(\sum_{i\in\text{決勝}} C_i\right)\)、最後の走査が \(O(M)\) なので全体で
[ O(N\log N + M + \sum_{i\in\text{決勝}} C_i) ] - 空間計算量: 食材カウント配列が \(O(M)\)、入力保持が合計で \(O\!\left(\sum C_i\right)\) 程度なので
[ O(M + \sum C_i) ]
実装のポイント
同点処理は「番号が小さい方が上位」なので、ソートキーを
(-V[i], i)にすると安全です。cntは食材番号が \(1 \sim M\) なので長さM+1の配列にすると扱いやすいです。\(\sum C_i\) が大きめなので、Python では
sys.stdin.buffer.readlineを使うと高速に読み込めます。ソースコード
import sys
def main():
input = sys.stdin.buffer.readline
N, M, K = map(int, input().split())
V = [0] * N
ing = [None] * N
for i in range(N):
a = list(map(int, input().split()))
V[i] = a[0]
C = a[1]
ing[i] = a[2:] if C else []
order = list(range(N))
order.sort(key=lambda i: (-V[i], i))
finalists = order[:K]
cnt = [0] * (M + 1)
for i in finalists:
for t in ing[i]:
cnt[t] += 1
ans = 0
for t in range(1, M + 1):
if cnt[t] == K:
ans += 1
print(ans)
if __name__ == "__main__":
main()
この解説は gpt-5.2-high によって生成されました。
投稿日時:
最終更新: