Official

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

  1. Compute the prefix maximum prefix_max[i]
  2. Find the positions of visible buildings and the total count base_visible
  3. If non-visible buildings exist, record base_visible as a candidate
  4. 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
  5. 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_visible as 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: