C - ビル街の眺望 / Skyline View Editorial by admin
gemini-3.5-flash-thinkingOverview
This problem asks us to maximize the number of buildings visible from the west (left side) when exactly one building is demolished from a row of \(N\) buildings.
A naive approach of “trying all buildings to demolish and counting visible buildings each time” would take \(O(N)\) per simulation and \(O(N^2)\) overall, which exceeds the time limit under the constraint \(N \le 2 \times 10^5\) (resulting in TLE).
Instead, by efficiently calculating how the number of visible buildings changes when each building is demolished, we can solve the problem in \(O(N \log N)\) total time complexity.
Analysis
1. Conditions for a Building to be Visible
Building \(i\) is visible to Takahashi if and only if “there is no building with height \(H_i\) or greater to the left of building \(i\) (from \(1\) to \(i-1\))”.
Let us define \(C_i\) as the number of buildings to the left of building \(i\) with height \(H_i\) or greater. - When \(C_i = 0\): Building \(i\) is originally visible. - When \(C_i \ge 1\): Building \(i\) is not visible because it is blocked by buildings in front.
2. Effect of Demolishing Building \(k\)
When building \(k\) is demolished, the total number of visible buildings changes as follows:
Effect on building \(k\) itself
- If building \(k\) was originally visible (\(C_k = 0\)), demolishing it decreases the number of visible buildings by \(1\).
- If it was not originally visible (\(C_k \ge 1\)), there is no decrease from this factor.
Effect on other buildings
- Among buildings \(i\) that were not originally visible, those that were “blocked only by building \(k\)” will become newly visible when building \(k\) is removed.
- “Blocked only by building \(k\)” means \(C_i = 1\) and the single blocking building is \(k\).
- Buildings with \(C_i \ge 2\) remain blocked by other buildings even after building \(k\) is demolished, so they do not become visible.
Therefore, if the total number of originally visible buildings is \(V\), the number of visible buildings after demolishing building \(k\) can be expressed as:
\[(\text{visible count after demolition}) = V - (\text{1 if building } k \text{ was originally visible, 0 otherwise}) + (\text{number of buildings that become newly visible by demolishing building } k)\]
If we can efficiently compute this for all \(k\) (\(1 \le k \le N\)), the maximum value is the answer.
Algorithm
To efficiently compute \(C_i\) (the number of buildings to the left with height greater than or equal to building \(i\)) for each building \(i\), we use a Fenwick Tree (Binary Indexed Tree, BIT) and coordinate compression.
Step 1: Coordinate Compression
Since building heights \(H_i\) can be as large as \(10^9\), they cannot be directly used as BIT indices. We focus on the number of distinct heights (at most \(N\) types) and convert them to integers from \(1\) to \(U\) (\(U \le N\)) through coordinate compression.
Step 2: Scan from Left to Right and Identify Blocking Buildings
We scan buildings \(i\) (\(0\) to \(N-1\)) from left to right, performing the following operations:
Computing \(C_i\), the number of buildings with height \(\ge H_i\)
- Using the BIT, we find the count of buildings processed so far (i.e., to the left) with height strictly less than \(H_i\). Call this
less. - Since the total number of buildings to the left is \(i\), the number of buildings with height \(\ge H_i\) is \(C_i = i - \text{less}\).
- Using the BIT, we find the count of buildings processed so far (i.e., to the left) with height strictly less than \(H_i\). Call this
Identifying the blocking building
- Let
max_valbe the maximum height among buildings seen so far, andmax_idxbe the index of that building. - If \(C_i = 1\), the single building blocking building \(i\) is the one with the maximum height so far, i.e.,
max_idx. Therefore, we incrementadd_count[max_idx]by \(1\). - Then, if the current building \(i\)’s height updates
max_val, we updatemax_valandmax_idx.
- Let
Updating the BIT
- Add the current building’s height to the BIT.
Step 3: Computing the Answer
Compute the total number of originally visible buildings \(V\) (the count of \(i\) where \(C_i = 0\)). For each building \(k\) (\(1 \le k \le N\)), calculate the value after demolishing it, and output the maximum.
Complexity
Time Complexity: \(O(N \log N)\)
- Sorting for coordinate compression takes \(O(N \log N)\).
- BIT queries and updates for each building take \(O(\log N)\), so the entire scan takes \(O(N \log N)\).
- The final aggregation is \(O(N)\).
- Overall, the complexity is \(O(N \log N)\), which runs in approximately \(0.2\) seconds even for \(N = 2 \times 10^5\).
Space Complexity: \(O(N)\)
- The map for coordinate compression, the BIT, and arrays storing information for each building (
C,add_count, etc.) use \(O(N)\) memory.
- The map for coordinate compression, the BIT, and arrays storing information for each building (
Implementation Notes
Switching between 1-indexed and 0-indexed
- Using 1-indexed for BIT operations and building numbers (\(1\) to \(N\)) helps reduce bugs. In the implementation code,
max_idxand the keys ofadd_countuse 1-indexed building numbers.
- Using 1-indexed for BIT operations and building numbers (\(1\) to \(N\)) helps reduce bugs. In the implementation code,
Identifying the blocking building when \(C_i = 1\)
When there is exactly one building to the left with height greater than or equal to the current building, that building is necessarily “the tallest building seen so far.” By leveraging this property, we can identify the blocking building by simply maintaining a single variable, without needing complex data structures.
Source Code
import sys
def solve():
input = sys.stdin.read
data = input().split()
if not data:
return
N = int(data[0])
H = [int(x) for x in data[1 : N + 1]]
# 座標圧縮
unique_H = sorted(list(set(H)))
h_to_compressed = {val: i + 1 for i, val in enumerate(unique_H)}
compressed_H = [h_to_compressed[x] for x in H]
U = len(unique_H)
bit = [0] * (U + 1)
def add(idx, val):
while idx <= U:
bit[idx] += val
idx += idx & -idx
def query(idx):
s = 0
while idx > 0:
s += bit[idx]
idx -= idx & -idx
return s
C = [0] * N
add_count = [0] * (N + 1)
max_val = 0
max_idx = -1
for i in range(N):
h = compressed_H[i]
less = query(h - 1)
c = i - less
C[i] = c
if c == 1:
add_count[max_idx] += 1
if h > max_val:
max_val = h
max_idx = i + 1
add(h, 1)
V = sum(1 for c in C if c == 0)
ans = 0
for k in range(1, N + 1):
is_visible = 1 if C[k - 1] == 0 else 0
val = V - is_visible + add_count[k]
if val > ans:
ans = val
print(ans)
if __name__ == "__main__":
solve()
This editorial was generated by gemini-3.5-flash-thinking.
posted:
last update: