D - フェンスの塗り替え / Repainting the Fence Editorial by admin
gpt-5.3-codex概要
塗り替え操作をそのまま順にシミュレーションすると遅すぎるため、操作を逆順に見て「まだ色が確定していない板だけを一度だけ塗る」ことで高速に解く問題です。
未確定の次の位置を高速に飛ばすために、Union-Find(次未処理位置管理)を使います。
考察
重要な観察は次の2つです。
- 板の最終色は「最後にその板を塗った操作」で決まる
- ならば操作を後ろから見ると、ある板に最初に出会った色がそのまま最終色になる
素朴に各操作で区間 \([L_i, R_i]\) を全部塗ると、最悪で
\(M \times N \approx 2\times10^5 \times 5\times10^5\) となり到底間に合いません。
逆順で考えると、「すでに色が確定した板」はもう触る必要がありません。
そこで「未確定の板のうち、位置 \(x\) 以上で最初のもの」をすぐ求められれば、確定済み区間をまとめて飛ばせます。
この“次の未確定位置へジャンプ”を実現するのが今回の Union-Find 的な配列 parent です。
parent[x] は「\(x\) が確定済みなら次に見るべき候補」を指すようにし、find(x) で代表(未確定の実位置)を取得します。
アルゴリズム
ans[1..N] = 0(未塗装)で初期化。parent[i] = iを用意し、番兵N+1も作る。- 操作列を逆順に処理する:
- 操作 \((l, r, c)\) に対して
x = find(l) x <= rの間:ans[x] = c(この板の最終色が確定)parent[x] = find(x+1)として、次回からxを飛ばせるようにするx = parent[x]で次の未確定板へ進む
- 操作 \((l, r, c)\) に対して
- 最後に
ans[1..N]を出力。
イメージ
例えば逆順で区間 \([2,5]\) を処理中に、3,4 が既に確定済みなら、find(2) の次は一気に 5 や 6 へ飛べます。
各板は確定時に1回だけ処理されるので高速です。
計算量
- 時間計算量: \(O((N+M)\alpha(N))\)(ほぼ線形)
- 空間計算量: \(O(N+M)\)(
ops,parent,ans)
実装のポイント
parentはN+2長で作り、N+1を番兵にする(範囲外アクセス防止)。findは経路圧縮を入れると高速化できる(コードでは while + 圧縮)。逆順処理が本質。順方向で同じことをしようとすると難しくなります。
M=0のときもそのままansは全部 0 で正しく出力されます。ソースコード
import sys
def main():
input = sys.stdin.readline
N, M = map(int, input().split())
ops = [tuple(map(int, input().split())) for _ in range(M)]
parent = list(range(N + 2)) # 1..N, sentinel N+1
ans = [0] * (N + 1)
def find(x):
while parent[x] != x:
parent[x] = parent[parent[x]]
x = parent[x]
return x
for l, r, c in reversed(ops):
x = find(l)
while x <= r:
ans[x] = c
parent[x] = find(x + 1)
x = parent[x]
print(*ans[1:])
if __name__ == "__main__":
main()
この解説は gpt-5.3-codex によって生成されました。
posted:
last update: