結果

問題 No.284 門松と魔法(2)
ユーザー qwewe
提出日時 2025-05-14 13:10:43
言語 PyPy3
(7.3.15)
結果
TLE  
実行時間 -
コード長 10,664 bytes
コンパイル時間 419 ms
コンパイル使用メモリ 82,688 KB
実行使用メモリ 181,420 KB
最終ジャッジ日時 2025-05-14 13:12:13
合計ジャッジ時間 10,297 ms
ジャッジサーバーID
(参考情報)
judge1 / judge3
このコードへのチャレンジ
(要ログイン)
ファイルパターン 結果
other AC * 23 TLE * 1 -- * 16
権限があれば一括ダウンロードができます

ソースコード

diff #

import sys

# Set higher recursion depth for safety, though iterative segment tree should be fine.
# sys.setrecursionlimit(200000) 

def solve():
    N = int(sys.stdin.readline())
    
    # Handle cases where N is too small to form a valid sequence of length >= 3.
    # The problem defines valid sequences as M=0 or M>=3 satisfying conditions.
    # Any sequence of length 1 or 2 is not valid according to this problem statement structure.
    # Thus, if N <= 2, the maximum possible valid length is 0.
    if N <= 2:
        # Consume the second line (array elements) if N > 0, as it's provided but not needed.
        if N > 0:
             sys.stdin.readline() 
        print(0)
        return
        
    # Read the bamboo heights
    A = list(map(int, sys.stdin.readline().split()))

    # Coordinate compression: Map original heights to ranks [0, D-1]
    # This is useful for using data structures like segment trees where array indices are needed.
    # Get unique sorted values
    coords = sorted(list(set(A)))
    # Create a dictionary mapping each height to its rank
    rank = {val: i for i, val in enumerate(coords)}
    # D is the number of distinct heights
    D = len(coords) 

    # Segment Tree Implementation 
    # Supports Range Maximum Query and Point Update operations.
    # Uses an iterative approach for potentially better performance and avoiding deep recursion.
    class SegmentTree:
        def __init__(self, size):
            # Calculate the smallest power of 2 greater than or equal to size for the tree array
            self.actual_size = 1
            while self.actual_size < size:
                 self.actual_size <<= 1
            # Initialize data array with 0s. Array size is 2 * power_of_2.
            self.data = [0] * (2 * self.actual_size)

        # Update leaf k (0-indexed) with value x. 
        # This update assumes we want to store the maximum length found so far for this state.
        # So we only update if the new value x is greater than the existing value.
        def update(self, k, x): 
            # Map k to the leaf index in the segment tree array
            k += self.actual_size
            
            # Optimization: If the new value is not greater, no update needed.
            if x <= self.data[k]:
                return

            self.data[k] = x
            # Propagate the change upwards to parent nodes
            while k > 1:
                # Parent node (k >> 1) stores the maximum of its children (k and k ^ 1)
                new_max = max(self.data[k], self.data[k ^ 1])
                # Optimization: If parent value doesn't change, stop propagation.
                if self.data[k >> 1] == new_max: 
                     break 
                self.data[k >> 1] = new_max
                k >>= 1
            
        # Query maximum value in the range [l, r) (0-indexed, exclusive of r)
        def query(self, l, r):
            # Handle invalid or empty range queries
            if l >= r: 
                 return 0
            
            # Map range [l, r) to segment tree array indices
            L = l + self.actual_size; R = r + self.actual_size
            s = 0 # Initialize maximum value found to 0
            
            # Iteratively traverse up the tree from leaves L and R
            while L < R:
                # If R is the right child, include its sibling node's range and move R left
                if R & 1:
                    R -= 1
                    s = max(s, self.data[R])
                # If L is the left child, include its value and move L right to its sibling
                if L & 1:
                    s = max(s, self.data[L])
                    L += 1
                # Move up to the parent level
                L >>= 1; R >>= 1
            return s

    # Store segment trees for each index k. segment_trees[k] will hold a tuple (ST_up_k, ST_down_k).
    # ST_up_k stores max dp_up[j][k] (length ending j,k with A[j]<A[k]) indexed by rank(A[j]).
    # ST_down_k stores max dp_down[j][k] (length ending j,k with A[j]>A[k]) indexed by rank(A[j]).
    # Note: This list could potentially consume large memory if N and D are large.
    segment_trees = [None] * N 
    
    # Variable to keep track of the maximum valid sequence length found so far (must be >= 3).
    max_len = 0

    # Iterate through each element A[k] which acts as the potential last element of a subsequence.
    for k in range(N): 
        
        # Initialize segment trees for the current index k.
        # These trees will store information about sequences ending at index k.
        st_up_k = SegmentTree(D)
        st_down_k = SegmentTree(D)
        
        A_k = A[k] # Height of the current last element
        rank_A_k = rank[A_k] # Rank of the height A[k]

        # Iterate through each element A[j] (where j < k) which acts as the potential second to last element.
        for j in range(k): 
            
            A_j = A[j] # Height of the second to last element
            rank_A_j = rank[A_j] # Rank of the height A[j]

            # Check if A_j and A_k are identical. 
            # A valid sequence requires distinct adjacent elements for the condition check (B_i != B_{i+1} implied).
            # Also, the triplet condition (B_{i-1}, B_i, B_{i+1}) requires all three heights to be distinct.
            # If A_j == A_k, this pair cannot be the last two elements of a valid sequence of length >= 3.
            if A_j == A_k:
                 continue # Skip this pair (j, k)

            # The base length for any pair (A_j, A_k) with A_j != A_k is 2.
            # This represents a potential starting point for a longer sequence.
            current_base_len = 2 

            # Initialize DP values for the current pair (j, k)
            current_dp_up = 0  # Max length ending A_j, A_k where A_j < A_k (A_k is peak relative to A_j)
            current_dp_down = 0 # Max length ending A_j, A_k where A_j > A_k (A_k is valley relative to A_j)
             
            # Retrieve the segment trees associated with index j, which contain info about sequences ending at j.
            prev_sts = segment_trees[j] 

            # Handle the base case where j=0. No previous element i exists. Can only form length 2 sequences.
            if prev_sts is None: 
                 if A_j < A_k:
                     current_dp_up = current_base_len
                 else: # A_j > A_k
                     current_dp_down = current_base_len
            
            # If j > 0, we can potentially extend sequences ending at j.
            else: 
                st_up_j, st_down_j = prev_sts # Unpack the trees for index j

                # Case 1: A_j < A_k. This implies A_k is potentially a peak.
                # The previous element A_j must have been a valley relative to (A_i, A_k).
                # This requires extending a sequence ending in state `down` at index j.
                if A_j < A_k: 
                    # Query max dp_down[i][j] from ST_down[j]. Conditions: i < j, A_i > A_j, and A_i != A_k.
                    # The query needs to cover ranks corresponding to heights > A_j, excluding A_k.
                    
                    # Query max length in height range (A_j, A_k) -> ranks (rank_A_j, rank_A_k)
                    max_val1 = st_down_j.query(rank_A_j + 1, rank_A_k) 
                    # Query max length in height range (A_k, infinity) -> ranks (rank_A_k + 1, D)
                    max_val2 = st_down_j.query(rank_A_k + 1, D)      
                    
                    # Find the maximum length among valid precursors
                    best_prev_len = max(max_val1, max_val2) 
                    
                    # If a valid precursor sequence of length >= 2 is found, we can extend it.
                    if best_prev_len >= 2: 
                        current_dp_up = max(current_base_len, best_prev_len + 1) 
                    else: # Otherwise, the best we can do is start a new sequence of length 2.
                         current_dp_up = current_base_len 

                # Case 2: A_j > A_k. This implies A_k is potentially a valley.
                # The previous element A_j must have been a peak relative to (A_i, A_k).
                # This requires extending a sequence ending in state `up` at index j.
                else: # A_j > A_k
                    # Query max dp_up[i][j] from ST_up[j]. Conditions: i < j, A_i < A_j, and A_i != A_k.
                    # The query needs to cover ranks corresponding to heights < A_j, excluding A_k.

                    # Query max length in height range (-infinity, A_k) -> ranks [0, rank_A_k)
                    max_val1 = st_up_j.query(0, rank_A_k)             
                    # Query max length in height range (A_k, A_j) -> ranks (rank_A_k + 1, rank_A_j)
                    max_val2 = st_up_j.query(rank_A_k + 1, rank_A_j) 
                    
                    # Find the maximum length among valid precursors
                    best_prev_len = max(max_val1, max_val2)

                    # If a valid precursor sequence of length >= 2 is found, extend it.
                    if best_prev_len >= 2: 
                        current_dp_down = max(current_base_len, best_prev_len + 1)
                    else: # Otherwise, start a new sequence of length 2.
                         current_dp_down = current_base_len
            
            # Update segment trees for index k with the computed lengths.
            # The update operation stores the max length sequence ending with (A_j, A_k)
            # at the position corresponding to rank(A_j) in the trees for k.
            if current_dp_up > 0:
                st_up_k.update(rank_A_j, current_dp_up)
                # If the new length is >= 3, update the overall maximum length found.
                if current_dp_up >= 3: max_len = max(max_len, current_dp_up)
            
            if current_dp_down > 0:
                st_down_k.update(rank_A_j, current_dp_down)
                # Update overall maximum length if >= 3.
                if current_dp_down >= 3: max_len = max(max_len, current_dp_down)

        # Store the pair of segment trees computed for index k. This information will be used
        # for subsequent indices `k' > k` when `k` acts as the second-to-last element `j`.
        segment_trees[k] = (st_up_k, st_down_k)

    # Final Result: The problem asks for the maximum M.
    # Valid M are 0 or M>=3 satisfying conditions.
    # If max_len found is less than 3, the maximum valid length is 0.
    if max_len < 3:
        print(0)
    else:
        print(max_len)

# Execute the solve function
solve()
0