C - 街灯の明るさ比べ / Comparing Streetlight Brightness 解説 by admin 印刷ページ
GPT 5.2 High概要
各時刻の「高橋の点灯数 − 青木の点灯数」を考え、その値が正になる時刻(\(1 \le t \le T\))の個数を、差分(いもす法)+座標圧縮で高速に数えます。
考察
各街灯は、点灯操作時刻を \(s\) とすると整数時刻 \(s, s+1, \dots, s+D-1\) の間だけ点灯します。
つまり「点灯している個数」は、時刻ごとに区間の重なり数(区間加算)になっています。
素朴に時刻 \(t=1\) から \(T\) まで全て調べると、\(T\) が最大 \(10^9\) なので不可能です(\(O(T)\) で TLE)。
ここで重要な観察は次の通りです。
- 明るさ(点灯数)は、ある時刻で急に変化するのは「点灯開始時刻」や「点灯終了の次の時刻」だけ。
- それ以外の時刻では、明るさは一定。
したがって、変化が起きる時刻だけをイベントとして集め、その間の区間長をまとめて足し込めばよいです。
さらに「高橋が明るい」かどうかだけ知りたいので、両者の点灯数を別々に管理する代わりに
差 \(diff(t) = (\text{高橋の点灯数}) - (\text{青木の点灯数})\) を管理すると簡単になります。
各街灯の寄与は次の区間加算です:
- 高橋の街灯(開始 \(a\))は \([a, a+D)\) に \(+1\)
- 青木の街灯(開始 \(b\))は \([b, b+D)\) に \(-1\)
このとき \(diff(t) > 0\) である時刻の総数を数えれば答えです。
アルゴリズム
差分配列(いもす法)を「時刻が巨大なので辞書で」行い、変化点だけソートして走査します。
- \(end\_cap = T+1\) とする(\([1, T]\) の整数時刻を数えるための右端番兵)。
- 連想配列
deltaを用意し、各区間 \([l, r)\) の加算 \(v\) をdelta[l] += vdelta[r] -= vで記録する。
- 高橋の各 \(a\) について、区間は \([a, \min(a+D, T+1))\) に \(+1\)。
- 青木の各 \(b\) について、区間は \([b, \min(b+D, T+1))\) に \(-1\)。
- 変化点として
deltaのキーに加え、必ず1とT+1も入れてソート(座標圧縮の役目)。 - 昇順に変化点
pos[i]を走査し、- 現在の差
diffにdelta[pos[i]]を加える - 次の変化点までの区間長は
pos[i+1] - pos[i] - もし
diff > 0なら、その区間長を答えに加算する
- 現在の差
- 得られた合計が、\(1 \le t \le T\) のうち高橋の方が明るい整数時刻の個数。
※整数時刻を数えているのに「区間長」を足してよい理由:
今回の時刻は整数で、差分は半開区間 \([p, q)\)(整数 \(p, p+1, \dots, q-1\))に一定なので、個数はちょうど \(q-p\) になります。
計算量
- 時間計算量: \(O((N+M)\log(N+M))\)(イベント数をソート)
- 空間計算量: \(O(N+M)\)(差分イベントを保持)
実装のポイント
点灯区間は \([s, s+D)\)(終了は \(s+D\))として扱うと、差分処理がきれいになります(\(D\) 個の整数時刻に対応)。
\(T\) を超える点灯は数えないので、終了点は必ず \(\min(s+D, T+1)\) に丸めます。
1とT+1を変化点に必ず入れることで、範囲端の取りこぼしを防げます。deltaは通常の配列ではなく辞書(連想配列)にして、必要な時刻だけ保持します。ソースコード
import sys
def main():
data = list(map(int, sys.stdin.buffer.read().split()))
N, M, D, T = data[0], data[1], data[2], data[3]
A = data[4:4 + N]
B = data[4 + N:4 + N + M]
end_cap = T + 1
delta = {}
def add(k, v):
delta[k] = delta.get(k, 0) + v
for a in A:
add(a, 1)
e = a + D
if e > end_cap:
e = end_cap
add(e, -1)
for b in B:
add(b, -1)
e = b + D
if e > end_cap:
e = end_cap
add(e, 1)
pos = list(delta.keys())
pos.append(1)
pos.append(end_cap)
pos.sort()
uniq = []
prev = None
for x in pos:
if x != prev:
uniq.append(x)
prev = x
pos = uniq
diff = 0
ans = 0
for i in range(len(pos) - 1):
p = pos[i]
diff += delta.get(p, 0)
if diff > 0:
ans += pos[i + 1] - p
print(ans)
if __name__ == "__main__":
main()
この解説は gpt-5.2-high によって生成されました。
投稿日時:
最終更新: