A - 旅行の立て替え精算 / Settling Travel Expenses 解説 by admin
Gemini 3.0 Flash (Thinking)概要
この問題は、旅行中に行われた複数の支払い(立て替え)について、最終的な各個人の収支バランス(受け取るべき金額、または支払うべき金額)を計算する問題です。
考察
各支払いにおいて、収支に影響を与える要素は以下の2点です。 1. 立て替えた人のプラス分: 支払い金額 \(C_i\) を全額出したため、その分だけ収支がプラスになります。 2. 利用者のマイナス分: 利用した \(K_i\) 人は、均等に割った額 \(\frac{C_i}{K_i}\) を負担するため、その分だけ収支がマイナスになります。
この問題のポイントは、「誰がどれだけ得をしたか・損をしたか」を一人ずつシミュレーションしても間に合うかという点です。 制約を見ると、人数 \(N\) や支払い回数 \(M\) は \(10^5\) です。また、全支払いの延べ利用者数 \(\sum K_i\) も \(10^5\) 以下となっています。 したがって、各支払いごとに「立て替えた人に加算」し、「利用者全員から減算」するという処理を愚直に行っても、全体の計算回数は \(M + \sum K_i\) 程度(約 \(2 \times 10^5\) 回)となり、制限時間内に十分に間に合います。
アルゴリズム
- 長さ \(N+1\) の配列(またはリスト) \(D\) を用意し、すべて \(0\) で初期化します。\(D[j]\) は人 \(j\) の現在の収支バランスを表します。
- 各支払い \(i = 1, 2, \ldots, M\) について以下の処理を行います。
- 立て替えた人 \(P_i\) の収支を更新する: \(D[P_i] \leftarrow D[P_i] + C_i\)
- 1人あたりの負担額を計算する: \(share = \frac{C_i}{K_i}\)
- 各利用者 \(B_{i,j}\) (\(j=1 \ldots K_i\)) について、収支を更新する: \(D[B_{i,j}] \leftarrow D[B_{i,j}] - share\)
- すべての支払いを処理した後、\(D[1]\) から \(D[N]\) までの値を順番に出力します。
計算量
- 時間計算量: \(O(N + M + \sum K_i)\)
- 配列の初期化に \(O(N)\)、各支払いの処理に \(O(1 + K_i)\) かかるため、全体の計算量は入力のサイズに対して線形です。
- 空間計算量: \(O(N)\)
- 各個人の収支を保持するための配列 \(D\) のサイズが \(N\) に比例します。
実装のポイント
高速な入出力: 入力データ数が最大で数十万個に及ぶため、Python標準の
input()を繰り返すと実行時間が長くなる可能性があります。sys.stdin.read().split()を使って一括で入力を取得し、リストから順に取り出す方法が効率的です。整数の扱い: 問題文で \(\frac{C_i}{K_i}\) が整数になることが保証されているため、切り捨て除算
//を使用して整数のまま計算することで、浮動小数点数による誤差を避けることができます。出力の効率化: \(N\) 行の出力を1つずつ
print()するよりも、文字列のリストに変換して'\n'.join()で一気に出力する方が高速です。ソースコード
import sys
def main():
# 旅行の立て替え精算の問題を解くためのメイン関数
# sys.stdin.read().split() を使用して、入力を一括で取得しトークンに分割します
# これにより、大量の入力データに対しても高速に処理することが可能です
input_data = sys.stdin.read().split()
# 入力が空の場合は終了します
if not input_data:
return
# グループの人数 N と支払い回数 M を取得
N = int(input_data[0])
M = int(input_data[1])
# 各人の精算バランスを格納するリスト D
# D[j] は人 j の「実際に立て替えた合計金額」から「本来負担すべき合計金額」を引いた値
# 人の番号は 1 から N であるため、サイズ N+1 のリストを用意します
D = [0] * (N + 1)
# 現在処理しているトークンのインデックス
ptr = 2
# M 回の支払い情報を順に処理
for _ in range(M):
# 立て替えた人の番号 P_i, 金額 C_i, 利用者数 K_i を取得
p_i = int(input_data[ptr])
c_i = int(input_data[ptr+1])
k_i = int(input_data[ptr+2])
ptr += 3
# 立て替えた人 P_i の実際に支払った金額を加算
D[p_i] += c_i
# 利用者一人あたりの負担額を計算 (C_i / K_i)
# 問題の制約により、この値は必ず整数になります
share = c_i // k_i
# 各利用者の本来負担すべき金額を減算
# 利用者のリスト B_{i,1}, ..., B_{i,K_i} を順に処理
for _ in range(k_i):
b_ij = int(input_data[ptr])
D[b_ij] -= share
ptr += 1
# 人 1 から N までの精算バランス D_j を文字列のリストに変換
results = map(str, D[1:])
# すべての結果を改行区切りで一気に出力
sys.stdout.write('\n'.join(results) + '\n')
if __name__ == '__main__':
main()
この解説は gemini-3-flash-thinking によって生成されました。
投稿日時:
最終更新: