C - ビル街の眺望 / Skyline View Editorial by admin
Claude 4.6 Opus (Thinking)Overview
This is a problem where you demolish one building out of \(N\) buildings to maximize the number of buildings visible from the west. A visible building is one that is “taller than all buildings to its west” (i.e., a position where the cumulative maximum from left to right is updated).
Analysis
Characteristics of Visible Buildings
Building \(i\) being visible means \(H_i > \max(H_1, \ldots, H_{i-1})\), i.e., it is a “position where a new record is set when scanning from left to right.”
Demolishing a Non-Visible Building
If building \(j\) is not visible, then \(H_j \leq \max(H_1, \ldots, H_{j-1})\). In this case, even if building \(j\) is removed, the “maximum value to the left” as seen from each building to the right of \(j\) does not change (since \(H_j\) is already less than or equal to the left-side maximum, removing it does not affect the maximum). Therefore, the set of visible buildings does not change, and the answer remains the original number of visible buildings (base_visible).
Demolishing a Visible Building
Let the sequence of visible buildings be \(p_0, p_1, \ldots, p_{K-1}\) (\(H_{p_0} < H_{p_1} < \cdots < H_{p_{K-1}}\), strictly increasing).
When \(p_m\) is demolished: - What is lost: \(p_m\) itself disappears, so \(-1\) - Affected range: Only the interval \((p_m, p_{m+1})\) between \(p_m\) and the next visible building \(p_{m+1}\). Buildings from \(p_{m+1}\) onward are unaffected since \(H_{p_{m+1}} > H_{p_m}\) - What is gained: Among the buildings in the interval \((p_m, p_{m+1})\), those that exceed the threshold \(\max(H_1, \ldots, H_{p_m - 1})\) and become “new records from left to right” become newly visible
Concrete example: For heights [3, 5, 4, 2, 4, 6], the visible buildings are at heights 3, 5, 6. If the building with height 5 is demolished, the threshold becomes 3, and scanning [4, 2, 4] within the interval, the first 4 sets a new record (4 > 3) but the second 4 does not. Thus gain = 1.
Efficient Computation
When calculating the gain from demolishing each visible building \(p_m\), we scan the interval \((p_m, p_{m+1})\). Since all intervals are non-overlapping, the total is \(O(N)\).
Algorithm
- Compute the prefix maximum
prefix_max[i] - Find the positions of visible buildings and the total count
base_visible - If non-visible buildings exist, record
base_visibleas a candidate - For each visible building \(p_m\) being demolished, scan the interval \((p_m, p_{m+1})\) to compute the gain:
- Threshold =
prefix_max[p_m](maximum value to the left of \(p_m\)) - Scan within the interval, counting buildings that exceed the threshold (updated during scanning)
- Answer candidate =
base_visible - 1 + gain
- Threshold =
- Output the maximum among all candidates
Complexity
- Time complexity: \(O(N)\) (prefix maximum computation is \(O(N)\), and the total scanning across all intervals is also \(O(N)\))
- Space complexity: \(O(N)\) (prefix maximum array, list of visible positions, etc.)
Implementation Notes
Case \(N = 1\): The only building must be demolished, so the answer is 0
When demolishing the last visible building \(p_{K-1}\): The right-end interval extends to \((p_{K-1}, N)\) (up to the end of the array)
Don’t forget to include
base_visibleas a candidate from removing a non-visible building (when gain is 0, demolishing a non-visible building is better than demolishing a visible one)Since the heights of visible buildings form a strictly increasing sequence, it is guaranteed that the visibility of buildings from \(p_{m+1}\) onward is not affected by the removal of \(p_m\)
Source Code
import sys
from bisect import bisect_left, insort
def main():
input_data = sys.stdin.buffer.read().split()
N = int(input_data[0])
H = [int(input_data[i+1]) for i in range(N)]
if N == 1:
# Must remove the only building, 0 visible
print(0)
return
# prefix_max[i] = max of H[0..i-1], prefix_max[0] = 0
prefix_max = [0] * (N + 1)
for i in range(N):
prefix_max[i+1] = max(prefix_max[i], H[i])
# A building i is visible (originally) if H[i] > prefix_max[i] (i.e., H[i] > max of all H[0..i-1])
# visible[i] = True if building i is visible in the original configuration
visible = [False] * N
for i in range(N):
if H[i] > prefix_max[i]:
visible[i] = True
base_visible = sum(visible)
# For each building j that we remove, we need to compute the number of visible buildings.
#
# If we remove building j:
# - For buildings i < j: visibility doesn't change (building j is to their east).
# - For buildings i > j: the prefix max changes (H[j] is removed from consideration).
#
# Let's think about what happens when we remove building j.
#
# For i > j: the new prefix max up to i (excluding j) is max of {H[k] : k < i, k != j}.
# Building i becomes visible iff H[i] > this new prefix max.
#
# prefix_max_without_j[i] for i > j:
# If H[j] < prefix_max[j] (j was not the unique maximum contributor), then removing j
# might not change prefix_max for positions after j... but it's not that simple.
#
# Let me think differently.
#
# second_prefix_max[i] = the second largest value in H[0..i-1] considering the running max perspective.
# Actually, let's track: for each position i, the prefix max excluding j depends on whether H[j] == prefix_max[j+1].
#
# Better approach: for each building j removed, the new prefix max at position i (i > j) is:
# max(prefix_max[j], suffix of max from j+1 to i-1)
# Wait, prefix_max[j] = max(H[0..j-1]).
# new_prefix_max_at_i = max(prefix_max[j], max(H[j+1..i-1]))
#
# So we need: for i > j, H[i] > max(prefix_max[j], max(H[j+1..i-1]))
# For each j, count visible buildings after removal:
# Buildings i < j that are visible: same as original (sum of visible[0..j-1]) minus (visible[j] if j counted... but j is removed so we subtract visible[j] from the left part? No, j is removed entirely)
#
# Left side (i < j): count of visible among 0..j-1 (unchanged) = sum(visible[0..j-1])
# But j itself is removed, so j doesn't count.
# Right side (i > j): need to recompute.
# For right side when removing j:
# new running max starts at prefix_max[j] (= max of H[0..j-1])
# Then for i = j+1, j+2, ..., N-1 in order, building i is visible if H[i] > current running max.
# This is O(N) per removal = O(N^2) total. Too slow for N=2e5.
# We need a smarter approach. Let's think about which buildings gain/lose visibility.
# For each j, the answer = (visible buildings among [0, j-1]) + (visible buildings among [j+1, N-1] with initial max = prefix_max[j])
# Precompute visible_prefix_sum
vis_prefix = [0] * (N + 1)
for i in range(N):
vis_prefix[i+1] = vis_prefix[i] + (1 if visible[i] else 0)
# For the right part, we need: given a starting max M = prefix_max[j], count buildings in [j+1, N-1] visible.
# A building i is visible from start M if H[i] > max(M, max(H[j+1..i-1])).
# Equivalently, H[i] > running max starting from M over H[j+1], H[j+2], ...
# The visible buildings are exactly those i > j where H[i] > max(M, all H[k] for j < k < i).
# These are the "new records" in the subsequence H[j+1..N-1] that also exceed M.
# The visible buildings in [j+1, N-1] with threshold M: these are the left-to-right maxima of H[j+1..N-1] that exceed M.
# Precompute: for each position i, the sorted list of left-to-right maxima values from i to N-1.
# Then answer = number of maxima from j+1 to N-1 that exceed prefix_max[j].
# Left-to-right maxima from position s to N-1: H[i] is a maximum if H[i] > max(H[s..i-1]).
# These depend on s, so we can't just precompute one list.
# But note: the left-to-right maxima starting from position 0 form a subset.
# If we start from position s, the maxima are: the first element H[s], then any H[i] > running max...
#
# Key insight: the left-to-right maxima of the entire array from position 0 are at positions p_1, p_2, ..., p_k.
# If we start from position s, the maxima are different.
#
# Alternative approach: suffix structure.
#
# Let's define the "record positions" from position s as those indices i >= s where H[i] > max(H[s..i-1]).
# The record positions from 0 are our originally visible buildings.
#
# From position j+1, the record positions are the left-to-right maxima of H[j+1], H[j+2], ..., H[N-1].
# Among these, we want those with H[i] > M = prefix_max[j].
# The left-to-right maxima from position j+1: these include H[j+1] (always a "record" as first element),
# then subsequent records. But we want those exceeding M.
# Actually let's think about it from the suffix perspective.
#
# From the right, let's compute for each position i, the "suffix records" looking left-to-right.
# Hmm, this is tricky.
# Let me try a different angle. Consider the left-to-right maxima of the full array (positions where visible[i] = True).
# Let these be at positions p_0 < p_1 < ... < p_{K-1} with heights H[p_0] < H[p_1] < ... < H[p_{K-1}].
# (They're strictly increasing since each is a new maximum.)
# When we remove building j:
# Case 1: j is not a visible building (visible[j] = False).
# Then the set of visible buildings doesn't lose j. But some buildings after j might become visible.
# Actually, if j is not visible, removing it doesn't change prefix_max for any position after j
# (since H[j] <= prefix_max[j], so prefix_max[j+1] = max(prefix_max[j], H[j]) = prefix_max[j]).
# Wait, that's not right. prefix_max[j+1] = max(prefix_max[j], H[j]). If H[j] = prefix_max[j],
# then prefix_max[j+1] = H[j] = prefix_max[j]. Removing j gives new prefix = prefix_max[j].
# So it doesn't change. But what if H[j] is not the prefix max but still blocks some building?
#
# Actually removing a non-visible building can still affect things if it was blocking something
# between two visible buildings... No wait. If j is not visible, H[j] <= prefix_max[j].
# So prefix_max[j+1] = max(prefix_max[j], H[j]) = prefix_max[j]. Removing j doesn't change
# the prefix max at j+1 or beyond. So visibility of buildings after j is unchanged.
#
# But we remove j from the count, and j wasn't visible anyway, so the answer when removing
# a non-visible building = base_visible (visible count among remaining N-1 buildings, unchanged).
#
# Wait, but base_visible counts visible buildings among all N. After removing j (non-visible),
# the visible buildings are the same set minus j, but j wasn't visible, so count = base_visible.
# But we also removed j from existence - could buildings after j that were hidden become visible?
#
# If j is not visible: H[j] <= prefix_max[j]. For any i > j, prefix_max_without_j[i] =
# max over {H[k] : k < i, k != j}. Since H[j] <= prefix_max[j] = max(H[0..j-1]), removing H[j]
# from the set doesn't reduce the maximum. So prefix_max_without_j[i] = prefix_max[i] for all i > j.
# Hmm wait, that's not quite right either. prefix_max[i] = max(H[0..i-1]). Removing j from [0,i-1]:
# max({H[k] : 0 <= k < i, k != j}). If H[j] < prefix_max[i], this doesn't change. If H[j] = prefix_max[i],
# it might change if H[j] is the unique maximum.
#
# Actually H[j] <= prefix_max[j] = max(H[0..j-1]). So for i > j, prefix_max[i] >= prefix_max[j] >= H[j].
# And the max of H[0..i-1] \ {j} >= prefix_max[j] >= H[j]. So yes, removing j doesn't change
# the prefix max for any position after j. So removing a non-visible building gives answer = base_visible.
# Case 2: j is a visible building (visible[j] = True).
# Removing j loses 1 visible building from the count. But may gain some buildings that become visible.
# H[j] > prefix_max[j], and H[j] = prefix_max[j+1].
# After removing j, the prefix max at positions after j changes.
# New prefix max at position i (for i > j) = max({H[k] : k < i, k != j}).
#
# Since j is a visible building (a record), H[j] > prefix_max[j].
# After removing j, the running max just before position j+1 becomes prefix_max[j] (instead of H[j]).
#
# So for positions i > j, we need to recompute visibility with the running max starting from prefix_max[j]
# instead of H[j] at position j.
#
# The buildings that might change visibility are those in [j+1, next_record - 1] where next_record
# is the next visible building after j (if any). Actually, the next visible building p after j has
# H[p] > H[j] > prefix_max[j], so p remains visible after removing j. And for buildings after p,
# the running max is >= H[p] > H[j], so removing j doesn't affect them.
#
# So the only buildings that can gain visibility are those between j and the next visible building.
# Among positions in (j, next_visible_after_j), the buildings that become visible are those that
# are "records" starting with threshold prefix_max[j] in the range (j, next_visible_after_j).
# Let me formalize:
# visible positions: p_0, p_1, ..., p_{K-1} (sorted).
# If we remove p_m (the m-th visible building):
# - We lose p_m from visible count: -1
# - Buildings between p_m and p_{m+1} (exclusive) might become visible.
# In this range, the new threshold is prefix_max[p_m] (= max of buildings before p_m).
# Before removal, the threshold was H[p_m] (which equals prefix_max[p_m + 1]).
# Buildings in range (p_m, p_{m+1}) were all non-visible, meaning H[i] <= H[p_m] for all i in this range.
# After removal, a building i in (p_m, p_{m+1}) is visible if H[i] > max(prefix_max[p_m], max(H[p_m+1..i-1] excluding p_m but p_m is already excluded since we're looking at i > p_m)).
# So: H[i] > running max starting from prefix_max[p_m] over the subsequence H[p_m+1], H[p_m+2], ..., H[i-1].
# The number of such "new records" in the range (p_m, p_{m+1}) with threshold prefix_max[p_m] is the gain.
# - For the last visible building p_{K-1}, the range extends to N-1.
# So answer when removing visible building p_m = base_visible - 1 + gain(m)
# where gain(m) = number of left-to-right maxima in H[p_m+1..p_{m+1}-1] (or H[p_m+1..N-1] if m=K-1)
# that exceed prefix_max[p_m].
# Since all H[i] in range (p_m, p_{m+1}) satisfy H[i] <= prefix_max[p_{m+1}] and the "records" among them
# starting with threshold prefix_max[p_m] are the ones we want...
# For non-visible building removal: answer = base_visible
# For visible building p_m removal: answer = base_visible - 1 + gain(m)
# We want the maximum over all choices.
# Non-visible gives base_visible (if there exists a non-visible building).
# Visible p_m gives base_visible - 1 + gain(m).
# So we need max gain(m) and check if base_visible - 1 + max_gain >= base_visible, i.e., max_gain >= 1.
# Now compute gain(m) efficiently.
# gain(m) = number of left-to-right records in segment (p_m, p_{m+1}) starting with threshold prefix_max[p_m].
#
# Since these buildings all have H[i] <= H[p_m] (they're between two consecutive records, the first being p_m
# with height H[p_m]), and prefix_max[p_m] < H[p_m], we need records exceeding prefix_max[p_m].
# To compute this efficiently, for each segment between consecutive visible buildings,
# we need to find the left-to-right maxima of that segment and count how many exceed a threshold.
# The left-to-right maxima of a segment can be found in O(segment length) time.
# Total over all segments = O(N) since segments partition [0, N-1] \ {visible positions}.
# Then for each segment, among its left-to-right maxima values, count those > prefix_max[p_m].
# Since left-to-right maxima are strictly increasing, we can binary search.
# Let's implement this.
vis_positions = [i for i in range(N) if visible[i]]
K = len(vis_positions)
best = 0
# Check non-visible removal
if N > K: # there exists a non-visible building
best = base_visible
# For each visible building removal
for m in range(K):
pm = vis_positions[m]
# Range of buildings to consider: (pm, next_vis) where next_vis = vis_positions[m+1] if m+1 < K else N
if m + 1 < K:
right_end = vis_positions[m + 1]
else:
right_end = N
# Compute left-to-right maxima in range (pm, right_end), i.e., positions pm+1, pm+2, ..., right_end-1
threshold = prefix_max[pm] # max of H[0..pm-1]
# Count records exceeding threshold
running_max = threshold
gain = 0
for i in range(pm + 1, right_end):
if H[i] > running_max:
gain += 1
running_max = H[i]
ans = base_visible - 1 + gain
if ans > best:
best = ans
# Edge case: if N == 1, we must remove the only building, answer = 0
# Already handled at the top.
# Also need to consider: what if we must remove a building and all are visible?
# Then K = N (all visible, meaning H is strictly increasing). We must remove one visible building.
# Removing the last one gives base_visible - 1.
# Removing an earlier one: gain from the segment between it and the next visible.
print(best)
main()
This editorial was generated by claude4.6opus-thinking.
posted:
last update: