B - 不満を感じる回数 / Number of Times Feeling Dissatisfied 解説 by admin
GPT 5.2 High概要
シールを配る過程で「出席番号 2 の高橋君がシールをもらった直後」に限って、高橋君より多くシールを持つ生徒が存在するかを判定し、その回数を数えます。
考察
高橋君が不満を感じるのは 「シールが高橋君(2)に渡された直後」 だけです。したがって各時点で必要なのは次の判定です:
- 高橋君の現在の枚数を \(c_2\)
- 高橋君以外の誰かの最大枚数を \(max\_other\)
- もし \(max\_other > c_2\) なら、そのタイミングで不満を \(1\) 回加算
ここで重要な気づきは、判定に必要なのは「全員の枚数の詳細」ではなく 「高橋君の枚数」と「他の生徒の最大枚数」だけ という点です。
素朴なアプローチが遅い理由
高橋君がシールをもらうたびに、
- 「全生徒を見て、高橋君より多い人がいるか」を毎回チェックすると
1 回のチェックに \(O(N)\) かかります。
最悪の場合、高橋君が \(O(M)\) 回呼ばれるので合計 \(O(NM)\) となり、\(N,M \le 3\times 10^5\) では到底間に合いません。
どう解決するか
各生徒の枚数は配るたびに 1 ずつ増えるだけなので、 - 高橋君の枚数 \(c_2\) - 「高橋君以外の最大枚数」\(max\_other\)
を逐次更新していけば、判定は毎回 \(O(1)\) でできます。
簡単な例
例えば途中で - 高橋君の枚数 \(c_2 = 2\) - 他の誰かが 3 枚持っていて \(max\_other = 3\)
このとき高橋君が次のシールをもらって \(c_2=3\) になっても、判定は
- \(max\_other(=3) > c_2(=3)\) は偽
なので不満は増えません。
逆に \(max\_other=4\) のままだったら - \(4 > 3\) なので不満が 1 回増えます。
アルゴリズム
- 配列
cntを用意し、cnt[x]を生徒 \(x\) の現在のシール枚数とする。 - 変数
max_otherを「高橋君(2)以外の生徒のシール枚数の最大値」として管理する。 - 配布順に \(P_1, P_2, \dots, P_M\) を走査する。
- もし
x == 2(高橋君)なら:cnt[2] += 1- もし
max_other > cnt[2]ならans += 1
- それ以外(他の生徒)なら:
cnt[x] += 1- もし
cnt[x] > max_otherならmax_other = cnt[x]に更新
- もし
ansを出力する。
max_other は「他の生徒が増える」タイミングでしか増えないため、常に正しく保てます。
計算量
- 時間計算量: \(O(M)\)(配布を 1 回ずつ処理し、各回 \(O(1)\))
- 空間計算量: \(O(N)\)(各生徒の枚数を保持)
実装のポイント
max_otherには 高橋君以外 の最大枚数のみを入れること(高橋君を混ぜると判定が壊れます)。判定は「高橋君がもらった直後」だけ行うこと(それ以外のタイミングで不満は増えない)。
入力が大きいので、Python では
sys.stdin.buffer.read()のような高速入力が有効です。ソースコード
import sys
def main():
data = list(map(int, sys.stdin.buffer.read().split()))
if not data:
return
N, M = data[0], data[1]
P = data[2:]
cnt = [0] * (N + 1)
max_other = 0
ans = 0
for x in P:
if x == 2:
cnt[2] += 1
if max_other > cnt[2]:
ans += 1
else:
cnt[x] += 1
if cnt[x] > max_other:
max_other = cnt[x]
print(ans)
if __name__ == "__main__":
main()
この解説は gpt-5.2-high によって生成されました。
投稿日時:
最終更新: