Official
C - グループ対抗勝ち抜き戦 / Group Knockout Battle Editorial by admin
deepseekv3概要
この問題は、\(N\)人の生徒をグループ分けして勝ち抜き戦を行い、最終的な優勝者を求める問題です。各グループでは実力値が最も高い生徒(同値の場合は出席番号が小さい生徒)が勝ち残ります。
考察
素朴なアプローチでは、各ラウンドで実際にグループ分けと勝者の選出をシミュレーションします。しかし、各ラウンドでグループの数は約 \(N/K\) 個になり、ラウンド数は \(\log_K N\) 回程度になるため、全体の計算量は \(O(N \log_K N)\) となります。\(N\) の最大値が \(500,000\) であり、\(K\) が大きい場合(例えば \(K\) が \(N\) に近い場合)はラウンド数が少なく効率的ですが、\(K\) が小さい場合(例えば \(K=2\))はラウンド数が \(\log_2 N \approx 19\) 回程度なので、十分に効率的です。
アルゴリズム
- すべての生徒を出席番号順(=入力順)にキューに格納します。各要素は(実力値, 出席番号)のタプルとします。
- キューに残っている生徒が1人になるまで以下を繰り返します:
- キューから先頭から \(K\) 人ずつ取り出してグループを形成します(最後のグループは \(K\) 人未満でも可)
- 各グループ内で最大の実力値を持つ生徒を選びます(同値の場合は出席番号が小さい生徒を選ぶ)
- 選ばれた勝者を新しいキューに追加します
- 最後に残った生徒の出席番号を出力します
計算量
- 時間計算量: \(O(N \log_K N)\)
- 各ラウンドの処理が \(O(N)\)、ラウンド数が \(O(\log_K N)\) のため
- 空間計算量: \(O(N)\)
- キューを使用するため
実装のポイント
dequeを使用することで、グループ分け時の要素の取り出しと追加を効率的に行えます各生徒を(実力値, 出席番号)のタプルで管理することで、
max関数を用いて簡単に勝者を選べますmax関数のキーとして(x[0], -x[1])を指定することで、実力値が最大で、同値の場合は出席番号が最小の生徒を選べますグループ分け時には、キューが空になるか、グループが \(K\) 人になるまで取り出しを行います
ソースコード
import sys
from collections import deque
def main():
data = sys.stdin.read().split()
n = int(data[0])
k = int(data[1])
A = list(map(int, data[2:2+n]))
students = deque()
for i in range(n):
students.append((A[i], i + 1))
while len(students) > 1:
new_round = deque()
group = []
while students:
group.append(students.popleft())
if len(group) == k or not students:
winner = max(group, key=lambda x: (x[0], -x[1]))
new_round.append(winner)
group = []
students = new_round
print(students[0][1])
if __name__ == "__main__":
main()
この解説は deepseekv3 によって生成されました。